import copy
import random
import math

from .agent import *
from .judge_condition import *
from .infer_paras import *
from .map import *
from copy import deepcopy

tank_attack_region = None  # 坦克的进攻任务需要一个全局变量
ifv_a = None  # 重型战车的进攻任务需要一个全局变量


class BT:
    def __init__(self):
        self.results = {'success': "success", 'fail': "fail", 'wait': "wait", 'error': "error"}
        self.children = {}
        self.run = None  # 把所有的动作都化简为run
        self.name = None
        self.current = None

    # 把结果转换为 self.results的结果， If false, return fail, anything else, return success
    def wrap(self, value):
        for k, v in self.results.items():
            if value == k:
                return v
        if not value:
            return self.results['fail']
        return self.results['success']

    def set_action(self, action):
        self.run = action
        self.name = str(action)

    def add_child(self, child):
        n = len(self.children)
        self.children[n + 1] = child

    def sequence(self, agent, obj_id):  # 顺序节点 有一个false就停
        for k, v in self.children.items():
            if self.wrap(v.run(agent, obj_id)) == self.results['fail']:
                return self.results['fail']
        return self.results['success']

    def select(self, agent, obj_id):  # 选择节点 有一个success就停
        for k, v in self.children.items():
            if self.wrap(v.run(agent, obj_id)) == self.results['success']:
                return self.results['success']
        return self.results['fail']

    def reversal(self, agent, obj_id):  # 反转节点 子节点返回False经过该节点返回True
        for k, v in self.children.items():
            if self.wrap(v.run(agent, obj_id)) == self.results['success']:
                return self.results['fail']
        return self.results['success']


def see_sum(agent, single, region, obj_id):  # "一个点对一个区域按照bop通视的数量"
    bop = agent.get_bop(obj_id)
    tmp_bop0 = deepcopy(bop)  # 深拷贝防止数据的更改
    tmp_bop1 = deepcopy(bop)
    tmp_bop0['cur_hex'] = single
    num = 0
    for i in region:
        tmp_bop1['cur_hex'] = i
        if agent.map.check_2bop_see(tmp_bop0, tmp_bop1, 0):
            num += 1
    return num


class UnmannedVehiclesDefendTree:
    """
        2022/10/24 20:08
        袁铎
        更新了行为树结构
        增加了部分判定函数
    """

    def __init__(self, defend_tar=None):
        """
        :param defend_tar: 防守目标，缺省是None，缺省时表示防守目标是所有夺控点
        """
        self.class_name = 'bt_'
        self.defend_tar = defend_tar
        self.bt = None
        self.defend_pos = None  # 用于存储推理出的最佳位置
        self.infer_flag = False  # 用于记录是否第一次运行推理 如果推理过最佳位置就不会第二次推理以节省时间
        self.finish = False  # 用于标记任务是否结束
        self.init_bt()
        self.actions = []

    # def get_bop(self, obj_id, observation):
    #     for bop in observation["operators"]:
    #         if obj_id == bop["obj_id"]:
    #             return bop

    def action_or_not(self, agent, obj_id):
        if ActionType.GuideShoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot_or_not(self, agent, obj_id):
        if ActionType.GuideShoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def reach_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.defend_pos:
            return True
        else:
            return False

    def pos_or_not(self, agent, obj_id):
        if self.defend_pos:
            return True
        else:
            return False

    def inference_defend_position(self, agent, obj_id):
        self.defend_pos = uv_infer_coord(agent, obj_id, self.defend_tar)
        return False

    def change_pos(self, agent, obj_id):
        self.defend_pos = change_ugv_pos(agent, obj_id)
        return True

    def shoot(self, agent, obj_id):
        candidates = []
        if agent.observation['valid_actions'][obj_id].get(ActionType.Shoot):
            candidates += agent.observation['valid_actions'][obj_id].get(ActionType.Shoot)
        elif agent.observation['valid_actions'][obj_id].get(ActionType.GuideShoot):
            candidates += agent.observation['valid_actions'][obj_id].get(ActionType.GuideShoot)
        action = agent.gen_shoot(obj_id, [])
        self.actions.append(action)
        return True

    def move(self, agent, obj_id):
        move_action = agent.gen_move(obj_id, self.defend_pos)  # TODO:当前坐标等于防御坐标
        if move_action:
            self.actions.append(move_action)
        return False

    def init_bt(self):
        # 第一个特殊节点:顺序节点
        order_node_1 = BT()

        # 是否输出行动
        action_or_not = BT()

        # 第二个特殊节点:选择节点
        select_node_2 = BT()

        # 三个顺序节点 两个选择节点
        order_node_3 = BT()
        order_node_4 = BT()
        select_node_5 = BT()
        select_node_6 = BT()
        order_node_7 = BT()

        # 当前能否实施射击 射击节点
        shoot_or_not = BT()
        shoot = BT()

        # 位置是否有值 推理适合防御位置 机动
        pos_or_not = BT()
        inference_defend_position = BT()
        move_for_defend = BT()

        # 是否到达位置 机动
        reach_pos_or_not = BT()
        move_continue = BT()

        # 是否遭遇打击/无人机 更换防御位置 机动
        be_attack_or_not = BT()
        change_position = BT()
        move_for_escape = BT()

        # 连接节点形成树
        order_node_1.add_child(action_or_not)
        order_node_1.add_child(select_node_2)

        select_node_2.add_child(order_node_3)
        select_node_2.add_child(order_node_4)

        order_node_3.add_child(shoot_or_not)
        order_node_3.add_child(shoot)

        order_node_4.add_child(select_node_5)
        order_node_4.add_child(select_node_6)
        order_node_4.add_child(order_node_7)

        select_node_5.add_child(pos_or_not)
        select_node_5.add_child(inference_defend_position)
        select_node_5.add_child(move_for_defend)

        select_node_6.add_child(reach_pos_or_not)
        select_node_6.add_child(move_continue)

        order_node_7.add_child(be_attack_or_not)
        order_node_7.add_child(change_position)
        order_node_7.add_child(move_for_escape)

        # 每个节点执行的动作
        # 顺序节点1
        order_node_1.set_action(order_node_1.sequence)
        # 是否输出行动
        action_or_not.set_action(self.action_or_not)
        # 选择节点2
        select_node_2.set_action(select_node_2.select)

        # 两个顺序节点 两个选择节点
        order_node_3.set_action(order_node_3.sequence)
        order_node_4.set_action(order_node_4.sequence)
        select_node_5.set_action(select_node_5.select)
        select_node_6.set_action(select_node_6.select)
        order_node_7.set_action(order_node_7.sequence)

        # 能否直接射击 射击节点
        shoot_or_not.set_action(self.shoot_or_not)  # 填入 能否射击 函数,返回 success 或 false
        shoot.set_action(self.shoot)  # 填入 射击 函数 返回success

        # 位置是否有值 推理适合防御位置 机动
        pos_or_not.set_action(self.pos_or_not)  # 填入 位置是否有值 函数,有返回success,无返回false
        inference_defend_position.set_action(
            self.inference_defend_position)  # 填入 推理合理防御位置 函数 ,返回false,并将推理出的位置填到self.position中
        move_for_defend.set_action(self.move)  # 填入 机动 函数,调用self.position当作目标点,返回false

        # 是否到达位置 机动
        reach_pos_or_not.set_action(self.reach_pos_or_not)  # 填入 是否到达位置 函数,是返回success,否返回false
        move_continue.set_action(self.move)  # 填入 机动 函数,调用self.position当作目标点,返回false

        # 是否遭遇打击/无人机 更换防御位置 机动
        be_attack_or_not.set_action(judge_unequal_hurt)  # 填入 判断是否遭遇打击/无人机 函数 返回success或false
        change_position.set_action(self.change_pos)  # 填入 推理新防御位置 函数,推理出的位置填到self.position中,并返回success
        move_for_escape.set_action(self.move)  # 填入 机动 函数,调用self.position当作目标点,返回false

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []
        return self.bt.run(agent, obj_id)


class CruiseMissilesTree:
    """
        2022/10/25 10:50
        袁铎
        添加了巡飞弹行为树
    """

    def __init__(self):
        self.class_name = 'bt_'
        self.bt = None
        self.target_pos = None  # 用于存储巡飞弹机动的目标点位
        self.target_id = None  # 用于存储巡飞弹射击目标id
        self.work_center = None  # 分配的参考中心点位
        self.init_bt()
        self.actions = []

    def act_or_not(self, agent, obj_id):
        # if ActionType.GuideShoot in agent.observation["valid_actions"][obj_id].keys():
        #  return True
        if not self.work_center:
            if not agent.situation.main_battlefield_refer_work_center:
                agent.situation.main_battlefield_refer_work_center = \
                    copy.deepcopy(agent.situation.main_battlefield_refer_work_center_standby)
            for i in agent.situation.main_battlefield_refer_work_center:
                self.work_center = i
                agent.situation.main_battlefield_refer_work_center.remove(i)
                break
        if ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    # 是否射击目标进入射程，有射击目标并且可射击返回True
    def target_in_range_or_not(self, agent, obj_id):
        # id只会在快好紧生命的时候才会有值
        if self.target_id and ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def live_or_not(self, agent, obj_id):  # 巡飞弹剩余生存时间判定
        bop = agent.get_bop(obj_id)
        if bop['alive_remain_time'] <= 150:
            return True
        else:
            return False

    def in_range_or_not(self, agent, obj_id):  # 判定目标是否在巡飞弹视野内
        bop_1 = agent.get_bop(obj_id)
        # bop_2=agent.get_bop(self.target)
        if self.target_id in bop_1['see_enemy_bop_ids']:
            return True
        else:
            return False

    def CruiseMissle_patrol_(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        self.target_pos = CruiseMissle_patrol1(agent, agent.situation.main_battlefield, bop['cur_hex'],
                                               self.work_center)
        print(f"===巡飞弹{obj_id}巡逻目标点位：{self.target_pos}")
        return False

    def CruiseMissle_enemy_choice_(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        self.target_pos, self.target_id = CruiseMissle_enemy_choice(agent, obj_id, bop['cur_hex'])
        if not self.target_id:
            return False
        print(f"===巡飞弹{obj_id}攻击目标点位：{self.target_pos}")
        return True

    def move(self, agent, obj_id):
        if ActionType.Move not in agent.observation["valid_actions"][obj_id].keys():
            return False
        # if self.target_pos is None:  # 测试效果，主战场没有消失敌人概率的点不机动
        #     return False
        # move_action, _, _ = agent.gen_move(obj_id, self.target_pos)  # 用这句有bug
        move_action = agent.gen_move(obj_id, self.target_pos)
        self.actions.append(move_action)
        return False

    def shoot(self, agent, obj_id):
        candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]  # 有时会出现引导射击
        self.actions.append(agent.gen_shoot(obj_id, candidate))
        return True

    def init_bt(self):
        # 顺序节点和选择节点
        order_node_0 = BT()
        select_node_add0 = BT()
        order_node_add0 = BT()
        order_node_1 = BT()
        select_node_2 = BT()
        select_node_3 = BT()
        select_node_4 = BT()

        # 是否能行动
        act_or_not = BT()

        # 是否射击目标进入射程 射击
        target_in_range_or_not = BT()
        shoot_add = BT()

        # 是否耗尽生命 确认巡逻点位 机动
        live_or_not = BT()
        confirm_cruise_pos = BT()
        move1 = BT()

        # 确认射击目标 确认巡逻点位 机动
        confirm_target = BT()
        confirm_cruise_pos1 = BT()
        move2 = BT()

        # 是否进入射程
        in_range_or_not = BT()
        move3 = BT()

        # 射击
        shoot = BT()

        # 连接节点形成树
        order_node_0.add_child(select_node_add0)
        order_node_0.add_child(order_node_1)

        select_node_add0.add_child(act_or_not)
        select_node_add0.add_child(order_node_add0)

        order_node_add0.add_child(target_in_range_or_not)
        order_node_add0.add_child(shoot_add)

        order_node_1.add_child(select_node_2)
        order_node_1.add_child(select_node_3)
        order_node_1.add_child(select_node_4)
        order_node_1.add_child(shoot)

        select_node_2.add_child(live_or_not)
        select_node_2.add_child(confirm_cruise_pos)
        select_node_2.add_child(move1)

        select_node_3.add_child(confirm_target)
        select_node_3.add_child(confirm_cruise_pos1)
        select_node_3.add_child(move2)

        select_node_4.add_child(in_range_or_not)
        select_node_4.add_child(move3)

        # 每个节点执行的动作
        # 顺序节点和选择节点
        order_node_0.set_action(order_node_0.sequence)
        select_node_add0.set_action(select_node_add0.select)
        order_node_add0.set_action(order_node_add0.sequence)
        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        select_node_3.set_action(select_node_3.select)
        select_node_4.set_action(select_node_4.select)

        # 是否能行动
        act_or_not.set_action(self.act_or_not)

        # 是否射击目标进入射程 射击
        target_in_range_or_not.set_action(self.target_in_range_or_not)
        shoot_add.set_action(self.shoot)

        # 是否耗尽生命 确认巡逻点位 机动
        live_or_not.set_action(self.live_or_not)  # 填入 巡飞弹是否耗尽生命 函数,是返回success,否返回false
        confirm_cruise_pos.set_action(
            self.CruiseMissle_patrol_)  # 填入 确认巡逻点位 函数,返回false:该函数需要判断是否有巡逻点位,没有就推理一个,有则判断是否到达巡逻点位,没有到达则巡逻点位不变,到达则推断新巡逻点位
        move1.set_action(self.move)  # 填入 机动 函数,返回false

        # 确认射击目标
        confirm_target.set_action(
            self.CruiseMissle_enemy_choice_)  # 填入 确认射击目标 函数,返回success:该函数判断是否有射击目标,有则直接返回,没有则推断射击目标
        confirm_cruise_pos1.set_action(self.CruiseMissle_patrol_)
        move2.set_action(self.move)

        # 是否进入射程
        in_range_or_not.set_action(self.in_range_or_not)  # 填入 判断目标是否进入射程 函数,进入则返回success,没有则返回false
        move3.set_action(self.move)  # 填入 机动 函数,返回false

        # 射击
        shoot.set_action(self.shoot)  # 填入 射击 函数,返回false

        self.bt = order_node_0

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []
        return self.bt.run(agent, obj_id)


class drone_tree:
    """
        2022/11/14 15:20
        袁铎
        更新了无人机行为树
    """

    def __init__(self):
        self.class_name = 'bt_'
        self.bt = None
        self.guide_shoot_pos = None  # 引导射击位置
        self.spot_pos = None  # 间瞄校射位置
        self.cruise_pos = None  # 用于存储推理出的巡逻点位
        # 引导射击目标?
        self.actions = []
        self.init_bt()

    def action_or_not(self, agent, obj_id):
        if ActionType.GuideShoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def guide_shoot_or_not(self, agent, obj_id):
        if ActionType.GuideShoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def guide_shoot(self, agent, obj_id):
        # TODO 需要检查射击的两个函数
        candidate = agent.observation["valid_actions"][obj_id][ActionType.GuideShoot]
        self.actions.append(agent.gen_guide_shoot(obj_id, candidate))
        return True

    def judge_infer_guide_shoot(self, agent, obj_id):
        self.guide_shoot_pos = judge_infer_guide_shoot(agent, obj_id)
        if self.guide_shoot_pos != False:
            return True
        else:
            return False

    def reach_guide_shoot_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.guide_shoot_pos:
            return True
        else:
            return False

    def spot_or_not(self, agent, obj_id):
        self.spot_pos = judge_guide_shoot(agent, obj_id)
        if self.spot_pos != False:
            return True
        else:
            return False

    def reach_spot_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.spot_pos:
            return True
        else:
            return False

    def confrim_patrol_pos(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        self.cruise_pos = UAV_patrol(agent, agent.situation.main_battlefield, bop['cur_hex'])
        return False

    def reach_cruise_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.cruise_pos:
            # print(f"===无人机当前位置: {bop['cur_hex']}")
            # print(f"===无人机目标位置: {self.cruise_pos}")
            return True
        else:
            # print(f"===无人机当前位置: {bop['cur_hex']}")
            # print(f"===无人机目标位置: {self.cruise_pos}")
            return False

    def move1(self, agent, obj_id):
        move_action, _, _ = agent.gen_move(obj_id, self.guide_shoot_pos)
        self.actions.append(move_action)
        print(f"===无人机引导射击移动")
        return True

    def move2(self, agent, obj_id):
        move_action, _, _ = agent.gen_move(obj_id, self.spot_pos)
        self.actions.append(move_action)
        print(f"===无人机间瞄校射移动")
        return True

    def move3(self, agent, obj_id):
        move_action, _, _ = agent.gen_move(obj_id, self.cruise_pos)
        self.actions.append(move_action)
        print(f"===无人机巡逻移动")
        return True

    def shoot(self, agent, obj_id):
        candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]
        self.actions.append(agent.gen_shoot(obj_id, candidate))
        return True

    def init_bt(self):
        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        order_node_3 = BT()
        order_node_4 = BT()
        order_node_5 = BT()
        select_node_6 = BT()
        select_node_7 = BT()

        # 能否机动或引导射击
        shoot_or_move_or_not = BT()

        # 是否能引导射击 引导射击
        guide_shoot_or_not = BT()
        guide_shoot = BT()

        # 判断引导射击条件&推理引导射击位置 是否到达位置 机动
        guide_shoot_determine = BT()
        reach_pos_or_not2 = BT()
        move2 = BT()

        # 判断间瞄校射条件&推理间瞄校射位置 是否到达位置 机动
        spot_or_not = BT()
        reach_pos_or_not1 = BT()
        move1 = BT()

        # 推理巡逻位置 是否到达位置 机动
        confrim_patrol_pos = BT()
        reach_pos_or_not3 = BT()
        move3 = BT()

        # 连接节点形成树
        order_node_1.add_child(shoot_or_move_or_not)
        order_node_1.add_child(select_node_2)

        select_node_2.add_child(order_node_3)
        select_node_2.add_child(order_node_4)
        select_node_2.add_child(order_node_5)
        select_node_2.add_child(confrim_patrol_pos)
        select_node_2.add_child(reach_pos_or_not3)
        select_node_2.add_child(move3)

        order_node_3.add_child(guide_shoot_or_not)
        order_node_3.add_child(guide_shoot)

        order_node_4.add_child(guide_shoot_determine)
        order_node_4.add_child(select_node_6)

        order_node_5.add_child(spot_or_not)
        order_node_5.add_child(select_node_7)

        select_node_6.add_child(reach_pos_or_not1)
        select_node_6.add_child(move1)

        select_node_7.add_child(reach_pos_or_not2)
        select_node_7.add_child(move2)

        # 每个节点执行的动作

        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        order_node_3.set_action(order_node_3.sequence)
        order_node_4.set_action(order_node_4.sequence)
        order_node_5.set_action(order_node_5.sequence)
        select_node_6.set_action(select_node_6.select)
        select_node_7.set_action(select_node_7.select)

        # 是否可以射击或引导射击
        shoot_or_move_or_not.set_action(self.action_or_not)

        # 是否能引导射击 引导射击
        guide_shoot_or_not.set_action(self.guide_shoot_or_not)
        guide_shoot.set_action(self.guide_shoot)

        # 判断引导射击条件&推理引导射击位置 是否到达位置 机动
        guide_shoot_determine.set_action(self.judge_infer_guide_shoot)
        reach_pos_or_not2.set_action(self.reach_guide_shoot_pos_or_not)
        move2.set_action(self.move1)

        # 判断间瞄校射条件&推理间瞄校射位置 是否到达位置 机动
        spot_or_not.set_action(self.spot_or_not)
        reach_pos_or_not1.set_action(self.reach_spot_pos_or_not)
        move1.set_action(self.move2)

        # 推理巡逻位置 是否到达位置 机动
        confrim_patrol_pos.set_action(self.confrim_patrol_pos)
        reach_pos_or_not3.set_action(self.reach_cruise_pos_or_not)
        move3.set_action(self.move3)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []
        return self.bt.run(agent, obj_id)


class TankDefenseTree:
    """
            2022/12/7 15:20
            吴欣娟
            更新了坦克行为树
    """

    def __init__(self, defense_target=None):
        self.class_name = 'bt'
        self.bt = None
        self.defense_scope = None  # 用于存储坦克防御区域
        self.defense_pos = None  # 用于存储坦克防御点位
        self.defense_target = defense_target
        self.init_bt()
        self.actions = []

    def move_or_shoot(self, agent, obj_id):
        if ActionType.Shoot in agent.observation['valid_actions'][obj_id].keys():
            return True
        elif ActionType.Move in agent.observation['valid_actions'][obj_id].keys():
            return True
        else:
            return False

    def shoot_or_not(self, agent, obj_id):
        if ActionType.Shoot in agent.observation['valid_actions'][obj_id].keys():
            return True
        else:
            return False

    def shoot(self, agent, obj_id):
        candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]
        self.actions.append(agent.gen_shoot(obj_id, candidate))
        return True

    def create_defense_scope(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if self.defense_target:
            self.defense_scope = agent.map.get_neighbors(self.defense_target)
        else:
            cities = [pos['coord'] for pos in agent.observation['cities']]
            distances = [agent.map.get_distance(city, bop['cur_hex']) for city in cities]
            city = cities[distances.index(max(distances))]
            if agent.map.get_distance(city, bop['cur_hex']) < 18:  # 与距离最远的夺控点距离小于18
                self.defense_scope = agent.city_neighbor_3
            else:
                self.defense_scope = None
        return True

    def move(self, agent, obj_id):
        move_action, _, _ = agent.gen_move(obj_id, self.defense_pos)
        self.actions.append(move_action)
        return True

    def infer_defense_pos(self, agent, obj_id):
        if agent.tank_defense_scope == self.defense_scope:
            self.defense_pos = agent.tank_defense_pos
        else:
            agent.tank_defense_scope = self.defense_scope
            self.defense_pos = Tank_defense_pos(agent, obj_id, self.defense_scope)
            agent.tank_defense_pos = self.defense_pos
        return True

    def not_reach_pos_or_not(self, agent, obj_id):  # 是否未到达防御点位，到达返回False，未到达返回True
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.defense_pos:
            return False
        else:
            return True

    def move_and_shoot(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop["cur_hex"] == self.defense_pos:  # 移动到行进间射击的另一个点位
            possible_poss = agent.map.get_neighbors(self.defense_pos)  # 可能移动的目标点位
            cover_poss = []  # 记录邻域中的掩蔽地形的点
            possible_enemies = []  # 筛选出的可能攻击坦克的车辆敌人，用于判断移动到敌人最少的格子
            see_enemy_nums = []  # 记录每个格子遇到的敌人数量
            min_enemy_num = 111  # 保证可以看到敌人并且敌人是最少的
            for i in agent.situation.enemy.values():  # 筛选出可能遇到的敌人
                if i["sub_type"] in [0, 1, 4] and agent.map.get_distance(i["coord"], bop["cur_hex"]) < 25:
                    possible_enemies.append(i["coord"])
            for i in possible_poss:  # 筛选出所有的掩蔽地形
                if agent.map.is_in_cover(i):
                    cover_poss.append(i)
            if not bop["see_enemy_bop_ids"]:  # 视野内没有敌人，也即攻击点位不可以射击
                if cover_poss:  # 如果有掩蔽地形，找到可以射击并且遇到敌人最少的
                    for i in cover_poss:
                        see_enemy_num = see_sum(agent, i, possible_enemies, obj_id)
                        if see_enemy_num == 0:
                            see_enemy_nums.append(111)
                        else:
                            see_enemy_nums.append(see_enemy_num)
                    min_enemy_num = min(see_enemy_nums)
                if cover_poss and min_enemy_num != 111:
                    index = see_enemy_nums.index(min_enemy_num)
                    destination = cover_poss[index]
                else:  # 如果没有掩蔽地形，找到可以射击遇到敌人最少的格子
                    for i in possible_poss:
                        see_enemy_num = see_sum(agent, i, possible_enemies, obj_id)
                        if see_enemy_num == 0:
                            see_enemy_nums.append(111)
                        else:
                            see_enemy_nums.append(see_enemy_num)
                    min_enemy_num = min(see_enemy_nums)
                    index = see_enemy_nums.index(min_enemy_num)
                    destination = possible_poss[index]  # todo: 这块儿偶尔报错IndexError: list index out of range
            else:  # 视野有敌人时候，攻击点位可以射击
                if cover_poss:  # 有掩蔽地形，找到最好能射击并且遇见敌人最少的
                    for i in cover_poss:
                        see_enemy_num = see_sum(agent, i, possible_enemies, obj_id)
                        if see_enemy_num == 0:
                            see_enemy_nums.append(111)
                        else:
                            see_enemy_nums.append(see_enemy_num)
                    min_enemy_num = min(see_enemy_nums)
                    index = see_enemy_nums.index(min_enemy_num)
                    destination = cover_poss[index]
                else:  # 没有掩蔽地形，找到遇见敌人最少的
                    for i in possible_poss:
                        see_enemy_num = see_sum(agent, i, possible_enemies, obj_id)
                        see_enemy_nums.append(see_enemy_num)
                    min_enemy_num = min(see_enemy_nums)
                    index = see_enemy_nums.index(min_enemy_num)
                    destination = possible_poss[index]
        else:
            destination = self.defense_pos  # 移动到推理的攻击点位
        move_action, _, _ = agent.gen_move(obj_id, destination)  # 需要更换成2022对应的
        if move_action:
            self.actions.append(move_action)
        return True

    # TODO:推理防御点位；行进间射机
    def init_bt(self):
        # 顺序节点和选择节点
        order_node_0 = BT()
        select_node_1 = BT()
        order_node_2 = BT()
        order_node_3 = BT()

        # 是否能机动或射击
        move_or_shoot = BT()

        # 是否能射击 射击
        shoot_or_not = BT()
        shoot = BT()

        # 生成防御区域目标 推理防御点位 是否抵达防御点位 机动
        confirm_defense_scope = BT()
        infer_defense_pos = BT()
        reach_pos_or_not = BT()
        move = BT()

        # 行进间射击
        move_and_shoot = BT()

        # 连接点形成树
        order_node_0.add_child(move_or_shoot)
        order_node_0.add_child(select_node_1)

        select_node_1.add_child(order_node_2)
        select_node_1.add_child(order_node_3)
        select_node_1.add_child(move_and_shoot)

        order_node_2.add_child(shoot_or_not)
        order_node_2.add_child(shoot)

        order_node_3.add_child(confirm_defense_scope)
        order_node_3.add_child(infer_defense_pos)
        order_node_3.add_child(reach_pos_or_not)
        order_node_3.add_child(move)

        # 每个节点执行的动作
        # 顺序节点和选择节点
        order_node_0.set_action(order_node_0.sequence)
        select_node_1.set_action(select_node_1.select)
        order_node_2.set_action(order_node_2.sequence)
        order_node_3.set_action(order_node_3.sequence)

        # 是否能机动或射击
        move_or_shoot.set_action(self.move_or_shoot)

        # 是否能射击 射击
        shoot_or_not.set_action(self.shoot_or_not)
        shoot.set_action(self.shoot)

        # 生成防御区域目标 推理防御点位 是否抵达防御点位 机动
        confirm_defense_scope.set_action(self.create_defense_scope)
        infer_defense_pos.set_action(self.infer_defense_pos)
        reach_pos_or_not.set_action(self.not_reach_pos_or_not)
        move.set_action(self.move)

        # 行进间射击
        move_and_shoot.set_action(self.move_and_shoot)

        self.bt = order_node_0

    def update_bt(self, agent, obj_id):
        self.actions = []
        return self.bt.run(agent, obj_id)


class TankDetectTree:
    """
    2022-11-27
    毕珊宁
    坦克侦查任务的行为树
    """

    def __init__(self, detect_tar=None):
        """

        :param detect_tar: 侦查目标点位
        """
        self.class_name = 'bttank_'
        self.bt = None
        self.detect_tar = detect_tar
        # self.spot_pos = None  # 巡逻目标点位
        self.actions = []
        self.init_bt()

    def action_or_not(self, agent, obj_id):  # 是否可以机动或射击
        if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot_or_not(self, agent, obj_id):  # 是否射击
        if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():  # 如果射击在有效动作中
            return True
        return False

    def shoot(self, agent, obj_id):  # 射击
        candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]
        self.actions.append(agent.gen_shoot(obj_id, candidate))
        return True

    def arrive_destination_or_not(self, agent, obj_id):  # 是否到达目标点位
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.detect_tar:  # 如果到达了目标点位
            print(f"===坦克当前位置: {bop['cur_hex']}")
            print(f"===坦克目标位置: {self.detect_tar}")
            return True
        return False

    def tank_mission_change(self, agent, obj_id):  # 坦克任务转防御任务
        # bop = agent.get_bop(obj_id)
        if agent.task[obj_id][0] == MissionType.Reconnaissance:  # 2-侦查
            agent.task[obj_id][0] = MissionType.Defense  # 1-防御  agent.task[obj_id]['mission_type']

    def move(self, agent, obj_id):  # 机动
        move_action, _, _ = agent.gen_move(obj_id, self.detect_tar)  # self.cruise_pos目标点位怎么定义？
        self.actions.append(move_action)
        return True

    def init_bt(self):
        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        order_node_3 = BT()
        order_node_4 = BT()

        # 能否机动或射击
        action_or_not = BT()

        # 能否射击 射击
        shoot = BT()
        shoot_or_not = BT()

        # 能否抵达目标点位 转坦克防御任务
        arrive_destination_or_not = BT()
        tank_mission_change = BT()

        #  机动
        move = BT()

        # 连接节点形成树
        order_node_1.add_child(action_or_not)
        order_node_1.add_child(select_node_2)

        select_node_2.add_child(order_node_3)
        select_node_2.add_child(order_node_4)
        select_node_2.add_child(move)

        order_node_3.add_child(shoot_or_not)
        order_node_3.add_child(shoot)

        order_node_4.add_child(arrive_destination_or_not)
        order_node_4.add_child(tank_mission_change)

        # 每个节点执行的动作

        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        order_node_3.set_action(order_node_3.sequence)
        order_node_4.set_action(order_node_4.sequence)

        # 是否能机动或射击
        action_or_not.set_action(self.action_or_not)  # 填入 巡飞弹是否耗尽生命 函数,是返回success,否返回false

        # 是否射击 射击
        shoot.set_action(self.shoot)  # 填入 射击函数 返回success
        shoot_or_not.set_action(self.shoot_or_not)  # 填入 能否射击 能返回success， 否返回false

        # 是否抵达目标点位  # 机动
        arrive_destination_or_not.set_action(self.arrive_destination_or_not)  # 填入 是否抵达目标点位 是返回success 否返回false
        tank_mission_change.set_action(self.tank_mission_change)  # 填入 转坦克防御任务 返回success

        # 机动
        move.set_action(self.move)  # 填入 机动函数 返回success

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []
        return self.bt.run(agent, obj_id)


class infantry_defence_tree:
    """
        2022/11/26 10:00
        袁铎
        更新了步兵防御行为树
    """

    def __init__(self):
        self.class_name = 'bt_'
        self.bt = None
        self.denfend_city_pos = None
        self.defence_pos = None  # 用于存储推理出的防御点位
        self.target_pos = None  #
        self.actions = []
        self.init_bt()

    # 推理防御位置
    def ensure_defence_pos(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if agent.get_bop('launcher'):
            if agent.request_for_ride:
                if obj_id in agent.request_for_ride.keys():
                    return True
        if self.defence_pos:
            return True
        if self.target_pos:
            return True
        if bop['car']:
            self.denfend_city_pos, self.defence_pos, self.target_pos = infantry_defence_infer_coord(agent, bop['car'],
                                                                                                    None)
        else:
            self.denfend_city_pos, self.defence_pos, self.target_pos = infantry_defence_infer_coord_without_car(agent,
                                                                                                                obj_id,
                                                                                                                None)
        agent.infantrt_defend_city.update({obj_id:self.denfend_city_pos})
        return True

    # 是否在车上
    def in_vehicle_or_not(self, agent, obj_id):
        #
        bop = agent.get_bop(obj_id)
        if bop['on_board'] == 0:
            return False
        else:
            return True

    # 夺控点是否能被占领
    def occupy_or_not(self, agent, obj_id):
        if self.denfend_city_pos:
            bop = agent.get_bop(obj_id)
            if bop['cur_hex'] == self.denfend_city_pos:
                return False
            city_nei = copy.deepcopy(agent.map.get_neighbors(self.denfend_city_pos))
            city_nei.append(self.denfend_city_pos)
            for i in agent.observation['cities']:
                if self.denfend_city_pos == i['coord']:
                    if i['flag'] != agent.color:
                        # 此处以后再加入判断夺控点周围是否有敌人的判断
                        for i in agent.observation['operators']:
                            if i['color'] != agent.color:
                                if i['cur_hex'] in city_nei:
                                    return False
                        return True
                    break
        return False

    # 为占领夺控点移动
    def move_for_occupy(self, agent, obj_id):
        if self.denfend_city_pos:
            move_action, _, _ = agent.gen_move(obj_id, self.denfend_city_pos)
            if move_action:
                self.actions.append(move_action)
            return False

    # 是否到达防御位置
    def reach_defence_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.defence_pos:
            return True
        else:
            return False

    # move
    def move1(self, agent, obj_id):
        move_action, _, _ = agent.gen_move(obj_id, self.defence_pos)
        if move_action:
            self.actions.append(move_action)
        return False

    # 是否能射击
    def shoot_or_not(self, agent, obj_id):
        if ActionType.GuideShoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    # 是否能掩蔽
    def take_cover_or_not(self, agent, obj_id):
        if 6 in agent.observation["valid_actions"][obj_id]:  # 有时候没有6
            for i in agent.observation["valid_actions"][obj_id][6]:
                if i['target_state'] == 4:
                    return True
        return False

    # 掩蔽
    def take_cover(self, agent, obj_id):
        self.actions.append({
            "actor": agent.seat,
            "obj_id": obj_id,
            "type": ActionType.ChangeState,
            "target_state": 4,
        })
        return False

    # 射击
    def shoot(self, agent, obj_id):
        candidate = None
        self.actions.append(agent.gen_shoot(obj_id, candidate))
        return False

    # 是否在运输中
    def in_transport_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if agent.request_for_ride:
            if obj_id in agent.request_for_ride.keys():
                # if agent.request_for_ride[obj_id]['transport_or_not'] == True & agent.request_for_ride[obj_id]['transport_or_not'] == True:
                #     return True
                # elif agent.request_for_ride[obj_id]['transport_or_not'] == True & agent.request_for_ride[obj_id]['transport_or_not'] == False:
                car_bop = agent.get_bop(bop['car'])
                # TODO:去测试一下下车时get_off_partner_id的内容
                if agent.request_for_ride[obj_id]['transport_or_not'] == True or obj_id in car_bop[
                    'get_off_partner_id'] or car_bop["get_off_remain_time"]:
                    return True
                    # if car_bop['get_off_partner_id']:  # 如果步兵正在下车 则等待下车完成
                    #     return False
                    # bop = agent.get_bop(obj_id)
                    # action = {
                    #     'actor': agent.seat,
                    #     'obj_id': bop['car'],
                    #     'type': 4,
                    #     'target_obj_id': obj_id
                    # }
                    # print('步兵下车跑步')
                    # return False
                else:
                    return False
            return False
        return False

    # 是否发送乘车请求
    def send_request_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if agent.map.get_distance(bop['cur_hex'], self.defence_pos) < 5:
            car_bop = agent.get_bop(bop['car'])
            if car_bop['get_off_partner_id']:  # 如果步兵正在下车 则等待下车完成
                return False
            bop = agent.get_bop(obj_id)
            action = {
                'actor': agent.seat,
                'obj_id': bop['car'],
                'type': 4,
                'target_obj_id': obj_id
            }
            print('步兵下车跑步')
            self.actions.append(action)
            return False
        else:
            return True

    # 发送乘车请求
    def send_request(self, agent, obj_id):
        if agent.request_for_ride:
            for i in agent.request_for_ride:
                if i == obj_id:
                    return False
        bop = agent.get_bop(obj_id)
        agent.request_for_ride.update({obj_id: {
            'denfend_city_pos': self.denfend_city_pos,
            'viches_id': bop['car'],
            'action_type': 'move',
            'target_pos': self.target_pos,
            'accept_or_not': False,
            'transport_or_not': False
        }})
        #
        return False

    def init_bt(self):
        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        select_node_3 = BT()
        order_node_4 = BT()
        order_node_5 = BT()
        select_node_6 = BT()
        select_node_7 = BT()
        order_node_8 = BT()

        select_node_occupy = BT()

        # 推理防御位置
        ensure_defence_pos = BT()

        # 是否在车上
        in_vehicle_or_not = BT()

        # 夺控点是否可以被占领
        occupy_or_not = BT()

        # 翻转节点
        reversal = BT()

        # 为占领夺控点移动
        move_for_occupy = BT()

        # 是否到达防御位置
        reach_defence_pos_or_not = BT()

        # 机动 !需要自己写
        move1 = BT()

        # 是否能射击
        shoot_or_not = BT()

        # 是否能掩蔽
        take_cover_or_not = BT()

        # 掩蔽
        take_cover = BT()

        # 射击
        shoot = BT()

        # 是否在运输中
        in_transport_or_not = BT()

        # 是否发送乘车请求
        send_request_or_not = BT()

        # 发送乘车请求
        send_request = BT()

        # 连接节点形成树
        order_node_1.add_child(ensure_defence_pos)
        order_node_1.add_child(select_node_2)
        order_node_1.add_child(select_node_3)

        select_node_2.add_child(in_vehicle_or_not)
        select_node_2.add_child(order_node_4)

        select_node_3.add_child(in_transport_or_not)
        select_node_3.add_child(order_node_5)

        order_node_4.add_child(select_node_occupy)
        order_node_4.add_child(select_node_6)
        order_node_4.add_child(select_node_7)
        order_node_4.add_child(shoot)

        select_node_occupy.add_child(reversal)
        select_node_occupy.add_child(move_for_occupy)

        reversal.add_child(occupy_or_not)

        order_node_5.add_child(send_request_or_not)
        order_node_5.add_child(send_request)

        select_node_6.add_child(reach_defence_pos_or_not)
        select_node_6.add_child(move1)

        select_node_7.add_child(shoot_or_not)
        select_node_7.add_child(order_node_8)

        order_node_8.add_child(take_cover_or_not)
        order_node_8.add_child(take_cover)

        # 每个节点执行的动作
        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        select_node_3.set_action(select_node_3.select)
        order_node_4.set_action(order_node_4.sequence)
        order_node_5.set_action(order_node_5.sequence)
        select_node_6.set_action(select_node_6.select)
        select_node_7.set_action(select_node_7.select)
        order_node_8.set_action(order_node_8.sequence)

        select_node_occupy.set_action(select_node_occupy.select)

        # 推理防御位置
        ensure_defence_pos.set_action(self.ensure_defence_pos)

        # 是否在车上
        in_vehicle_or_not.set_action(self.in_vehicle_or_not)

        # 夺控点是否可以被占领
        occupy_or_not.set_action(self.occupy_or_not)

        # 反转节点
        reversal.set_action(reversal.reversal)

        # 移动以占领夺控点
        move_for_occupy.set_action(self.move_for_occupy)

        # 是否到达防御位置
        reach_defence_pos_or_not.set_action(self.reach_defence_pos_or_not)

        # 机动
        move1.set_action(self.move1)

        # 是否能射击
        shoot_or_not.set_action(self.shoot_or_not)

        # 是否能掩蔽
        take_cover_or_not.set_action(self.take_cover_or_not)

        # 掩蔽
        take_cover.set_action(self.take_cover)

        # 射击
        shoot.set_action(self.shoot)

        # 是否在运输中
        in_transport_or_not.set_action(self.in_transport_or_not)

        # 是否发送乘车请求
        send_request_or_not.set_action(self.send_request_or_not)

        # 发送乘车请求
        send_request.set_action(self.send_request)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []
        return self.bt.run(agent, obj_id)


class infantry_attack_tree:
    """
        2022/11/26 10:00
        袁铎
        更新了步兵防御行为树
    """

    def __init__(self):
        self.class_name = 'bt_'
        self.bt = None
        self.denfend_city_pos = None
        self.defence_pos = None  # 用于存储推理出的防御点位
        self.target_pos = None  #
        self.actions = []
        self.init_bt()

    # 推理防御位置
    def ensure_defence_pos(self, agent, obj_id):
        if agent.request_for_ride:
            if obj_id in agent.request_for_ride.keys():
                return True
        if self.defence_pos:
            return True
        bop = agent.get_bop(obj_id)
        if bop['car']:
            self.denfend_city_pos, self.defence_pos, self.target_pos = infantry_attack_infer_coord(agent, bop['car'], None);
        else:
            self.denfend_city_pos, self.defence_pos = infantry_attack_infer_coord_without_car(agent, obj_id, None);
        return True

    # 是否在车上
    def in_vehicle_or_not(self, agent, obj_id):
        #
        bop = agent.get_bop(obj_id)
        if bop['on_board'] == 0:
            return False
        else:
            return True

    # 是否到达防御位置
    def reach_defence_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.defence_pos:
            return True
        else:
            return False

    # move
    def move1(self, agent, obj_id):
        move_action, _, _ = agent.gen_move(obj_id, self.defence_pos)
        if move_action:
            self.actions.append(move_action)
        return False

    # 是否能射击
    def shoot_or_not(self, agent, obj_id):
        if ActionType.GuideShoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    # 是否能掩蔽
    def take_cover_or_not(self, agent, obj_id):
        if 6 in agent.observation["valid_actions"][obj_id]:  # 有时候没有6
            for i in agent.observation["valid_actions"][obj_id][6]:
                if i['target_state'] == 4:
                    return True
        return False

    # 掩蔽
    def take_cover(self, agent, obj_id):
        self.actions.append({
            "actor": agent.seat,
            "obj_id": obj_id,
            "type": ActionType.ChangeState,
            "target_state": 4,
        })
        return False

    # 射击
    def shoot(self, agent, obj_id):
        candidate = None
        # candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]
        self.actions.append(agent.gen_shoot(obj_id, candidate))
        return False

    # 是否在运输中
    def in_transport_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if agent.request_for_ride:
            if obj_id in agent.request_for_ride.keys():
                # if agent.request_for_ride[obj_id]['transport_or_not'] == True & agent.request_for_ride[obj_id]['transport_or_not'] == True:
                #     return True
                # elif agent.request_for_ride[obj_id]['transport_or_not'] == True & agent.request_for_ride[obj_id]['transport_or_not'] == False:
                car_bop = agent.get_bop(bop['car'])
                # TODO:去测试一下下车时get_off_partner_id的内容
                if agent.request_for_ride[obj_id]['transport_or_not'] == True or obj_id in car_bop[
                    'get_off_partner_id'] or car_bop["get_off_remain_time"]:
                    return True
                    # if car_bop['get_off_partner_id']:  # 如果步兵正在下车 则等待下车完成
                    #     return False
                    # bop = agent.get_bop(obj_id)
                    # action = {
                    #     'actor': agent.seat,
                    #     'obj_id': bop['car'],
                    #     'type': 4,
                    #     'target_obj_id': obj_id
                    # }
                    # print('步兵下车跑步')
                    # return False
                else:
                    return False
            return False
        return False

    # 是否发送乘车请求
    def send_request_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if agent.map.get_distance(bop['cur_hex'], self.defence_pos) < 5:
            car_bop = agent.get_bop(bop['car'])
            if car_bop['get_off_partner_id']:  # 如果步兵正在下车 则等待下车完成
                return False
            bop = agent.get_bop(obj_id)
            action = {
                'actor': agent.seat,
                'obj_id': bop['car'],
                'type': 4,
                'target_obj_id': obj_id
            }
            print('步兵下车跑步')
            self.actions.append(action)
            return False
        else:
            return True

    # 发送乘车请求
    def send_request(self, agent, obj_id):
        if agent.request_for_ride:
            for i in agent.request_for_ride:
                if i == obj_id:
                    return False
        bop = agent.get_bop(obj_id)
        agent.request_for_ride.update({obj_id: {
            'denfend_city_pos': self.denfend_city_pos,
            'viches_id': bop['car'],
            'action_type': 'move',
            'target_pos': self.target_pos,
            'accept_or_not': False,
            'transport_or_not': False
        }})
        #
        return False

    def init_bt(self):
        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        select_node_3 = BT()
        order_node_4 = BT()
        order_node_5 = BT()
        select_node_6 = BT()
        select_node_7 = BT()
        order_node_8 = BT()

        # 推理防御位置
        ensure_defence_pos = BT()

        # 是否在车上
        in_vehicle_or_not = BT()

        # 是否到达防御位置
        reach_defence_pos_or_not = BT()

        # 机动
        move1 = BT()

        # 是否能射击
        shoot_or_not = BT()

        # 是否能掩蔽
        take_cover_or_not = BT()

        # 掩蔽
        take_cover = BT()

        # 射击
        shoot = BT()

        # 是否在运输中
        in_transport_or_not = BT()

        # 是否发送乘车请求
        send_request_or_not = BT()

        # 发送乘车请求
        send_request = BT()

        # 连接节点形成树
        order_node_1.add_child(ensure_defence_pos)
        order_node_1.add_child(select_node_2)
        order_node_1.add_child(select_node_3)

        select_node_2.add_child(in_vehicle_or_not)
        select_node_2.add_child(order_node_4)

        select_node_3.add_child(in_transport_or_not)
        select_node_3.add_child(order_node_5)

        order_node_4.add_child(select_node_6)
        order_node_4.add_child(select_node_7)
        order_node_4.add_child(shoot)

        order_node_5.add_child(send_request_or_not)
        order_node_5.add_child(send_request)

        select_node_6.add_child(reach_defence_pos_or_not)
        select_node_6.add_child(move1)

        select_node_7.add_child(shoot_or_not)
        select_node_7.add_child(order_node_8)

        order_node_8.add_child(take_cover_or_not)
        order_node_8.add_child(take_cover)

        # 每个节点执行的动作
        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        select_node_3.set_action(select_node_3.select)
        order_node_4.set_action(order_node_4.sequence)
        order_node_5.set_action(order_node_5.sequence)
        select_node_6.set_action(select_node_6.select)
        select_node_7.set_action(select_node_7.select)
        order_node_8.set_action(order_node_8.sequence)

        # 推理防御位置
        ensure_defence_pos.set_action(self.ensure_defence_pos)

        # 是否在车上
        in_vehicle_or_not.set_action(self.in_vehicle_or_not)

        # 是否到达防御位置
        reach_defence_pos_or_not.set_action(self.reach_defence_pos_or_not)

        # 机动
        move1.set_action(self.move1)

        # 是否能射击
        shoot_or_not.set_action(self.shoot_or_not)

        # 是否能掩蔽
        take_cover_or_not.set_action(self.take_cover_or_not)

        # 掩蔽
        take_cover.set_action(self.take_cover)

        # 射击
        shoot.set_action(self.shoot)

        # 是否在运输中
        in_transport_or_not.set_action(self.in_transport_or_not)

        # 是否发送乘车请求
        send_request_or_not.set_action(self.send_request_or_not)

        # 发送乘车请求
        send_request.set_action(self.send_request)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []
        return self.bt.run(agent, obj_id)


class TankAttackTree:

    def __init__(self, attack_tar=None):
        """
        :param attack_tar: 攻击目标，缺省是None，缺省时表示攻击目标是所有夺控点
        """
        self.class_name = 'bt_'
        self.attack_tar = attack_tar  # 需要攻击的目标中心（传入的）
        self.tank_attack_region = None  # 坦克的进攻区域
        self.attack_pos = None  # 推理出的攻击位置
        self.visual_enemy_region = {}  # 用于记录进攻目标区域敌人的停留信息
        self.attack_over = None  # 为True时候表示需要传入新的防御点位
        self.bt = None
        self.init_bt()
        self.actions = []  # 坦克进攻任务输出的行动

    def shoot_or_move_or_not(self, agent, obj_id):
        if self.visual_enemy_region:  # 更新敌人在进攻目标区域内的时间信息
            for operator in agent.observation["operators"]:
                if operator["color"] == agent.color:
                    continue
                if operator["cur_hex"] in self.visual_enemy_region.keys():
                    self.visual_enemy_region[operator["cur_hex"]] = agent.observation["time"]["cur_step"]
        if ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot_or_not(self, agent, obj_id):
        if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot(self, agent, obj_id):
        candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]
        action = agent.gen_shoot(obj_id, candidate)
        if action:
            self.actions.append(action)
        return True

    def gen_tank_attack_region(self, agent, obj_id):
        #  需要增加进攻区域的修改，点位的删除
        bop = agent.get_bop(obj_id)
        global tank_attack_region
        tank_attack_region = []
        if bop['cur_hex'] == self.attack_pos:  # 已经到达进攻点位的时候进行进攻区域的修改（不能修改的太频繁导致坦克的徘徊）
            for i in self.tank_attack_region:
                if not agent.pos_is_see(i):  # 未知的点不剔除
                    tank_attack_region.append(i)  # 保留不确定的点
                    continue  # remove会影响for循环！！！
                if agent.observation["time"]["cur_step"] - self.visual_enemy_region[i] < 45:
                    tank_attack_region.append(i)  # 保留敌人45秒逗留过的点
            if not tank_attack_region:
                self.attack_over = True  # 进攻区域清空，坦克的一次进攻任务结束需要下达新的作战任务
            return False
        if not self.attack_pos:  # 首次进入生成目标区域
            # (添加后程序self.tank_attack_region = tank_attack_region会把作战区域清空，现在的程序要求每时刻都有推理的区域)
            if self.attack_tar:
                tank_attack_region.append(self.attack_tar)
                tank_attack_region += agent.map.get_neighbors(self.attack_tar)
            else:
                tank_attack_region = copy.deepcopy(agent.city_neighbor_3)  # 对应agent中不改变的量要用深拷贝，防备造成干扰，比如区域的修改
        else:
            tank_attack_region = copy.deepcopy(self.tank_attack_region)
        if not self.visual_enemy_region:
            for i in tank_attack_region:
                self.visual_enemy_region[i] = agent.observation["time"]["cur_step"]
        return False

    def infer_tank_attack_pos(self, agent, obj_id):
        if tank_attack_region == self.tank_attack_region and self.attack_pos:  # 目标区域没有修改
            return False  # 返回之前推理的点（之前returnattackpos）
        else:  # 目标区域修改后重新推理点位
            self.tank_attack_region = tank_attack_region
            attack_pos = infer_tank_attack_pos(agent, obj_id, tank_attack_region, self.attack_tar)
            if attack_pos is None:
                self.attack_over = True  # 没有合理的进攻点位，坦克的一次进攻任务结束需要下达新的作战任务
            else:
                self.attack_pos = attack_pos
        return False

    def reach_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.attack_pos:
            return True
        else:
            return False

    def move(self, agent, obj_id):
        action, _, _ = agent.gen_move(obj_id, self.attack_pos)  # 需要更换成2022对应的
        if action:
            self.actions.append(action)
        return False

    def move_and_shoot(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop["cur_hex"] == self.attack_pos:  # 移动到行进间射击的另一个点位
            possible_poss = agent.map.get_neighbors(self.attack_pos)  # 可能移动的目标点位
            cover_poss = []  # 记录邻域中的掩蔽地形的点
            possible_enemies = []  # 筛选出的可能攻击坦克的车辆敌人，用于判断移动到敌人最少的格子
            see_enemy_nums = []  # 记录每个格子遇到的敌人数量
            min_enemy_num = 111  # 保证可以看到敌人并且敌人是最少的
            for i in agent.situation.enemy.values():  # 筛选出可能遇到的敌人
                if i["sub_type"] in [0, 1, 4] and agent.map.get_distance(i["coord"], bop["cur_hex"]) < 25:
                    possible_enemies.append(i["coord"])
            for i in possible_poss:  # 筛选出所有的掩蔽地形
                if agent.map.is_in_cover(i):
                    cover_poss.append(i)
            if not bop["see_enemy_bop_ids"]:  # 视野内没有敌人，也即攻击点位不可以射击
                if cover_poss:  # 如果有掩蔽地形，找到可以射击并且遇到敌人最少的
                    for i in cover_poss:
                        see_enemy_num = see_sum(agent, i, possible_enemies, obj_id)
                        if see_enemy_num == 0:
                            see_enemy_nums.append(111)
                        else:
                            see_enemy_nums.append(see_enemy_num)
                    min_enemy_num = min(see_enemy_nums)
                if cover_poss and min_enemy_num != 111:
                    index = see_enemy_nums.index(min_enemy_num)
                    destination = cover_poss[index]
                else:  # 如果没有掩蔽地形，找到可以射击遇到敌人最少的格子
                    see_enemy_nums = []  # 当不满足上个if时候需要清空，不然会把有掩蔽地形的点添加两次
                    for i in possible_poss:
                        see_enemy_num = see_sum(agent, i, possible_enemies, obj_id)
                        if see_enemy_num == 0:
                            see_enemy_nums.append(111)
                        else:
                            see_enemy_nums.append(see_enemy_num)
                    min_enemy_num = min(see_enemy_nums)
                    index = see_enemy_nums.index(min_enemy_num)
                    destination = possible_poss[index]
            else:  # 视野有敌人时候，攻击点位可以射击
                if cover_poss:  # 有掩蔽地形，找到最好能射击并且遇见敌人最少的
                    for i in cover_poss:
                        see_enemy_num = see_sum(agent, i, possible_enemies, obj_id)
                        if see_enemy_num == 0:
                            see_enemy_nums.append(111)
                        else:
                            see_enemy_nums.append(see_enemy_num)
                    min_enemy_num = min(see_enemy_nums)
                    index = see_enemy_nums.index(min_enemy_num)
                    destination = cover_poss[index]
                else:  # 没有掩蔽地形，找到遇见敌人最少的
                    for i in possible_poss:
                        see_enemy_num = see_sum(agent, i, possible_enemies, obj_id)
                        see_enemy_nums.append(see_enemy_num)
                    min_enemy_num = min(see_enemy_nums)
                    index = see_enemy_nums.index(min_enemy_num)
                    destination = possible_poss[index]
        else:
            destination = self.attack_pos  # 移动到推理的攻击点位
        action, _, _ = agent.gen_move(obj_id, destination)  # 需要更换成2022对应的
        if action:
            self.actions.append(action)
        return True

    def init_bt(self):

        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        order_node_3 = BT()
        order_node_4 = BT()
        select_node_5 = BT()

        # 是否可机动或射击
        shoot_or_move_or_not = BT()

        # 是否能射击 射击
        shoot_or_not = BT()
        shoot = BT()

        # 生成进攻目标区域
        gen_tank_attack_region = BT()

        # 推理进攻点位
        infer_tank_attack_pos = BT()

        # 是否抵达进攻点位 机动
        reach_pos_or_not = BT()
        move = BT()

        # 行进间射击
        move_and_shoot = BT()

        # 连接节点形成树
        order_node_1.add_child(shoot_or_move_or_not)
        order_node_1.add_child(select_node_2)

        select_node_2.add_child(order_node_3)
        select_node_2.add_child(gen_tank_attack_region)
        select_node_2.add_child(infer_tank_attack_pos)
        select_node_2.add_child(order_node_4)

        order_node_3.add_child(shoot_or_not)
        order_node_3.add_child(shoot)

        order_node_4.add_child(select_node_5)
        order_node_4.add_child(move_and_shoot)

        select_node_5.add_child(reach_pos_or_not)
        select_node_5.add_child(move)

        # 节点动作的添加
        # 顺序和选择节点的添加
        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        order_node_3.set_action(order_node_3.sequence)
        order_node_4.set_action(order_node_4.sequence)
        select_node_5.set_action(select_node_5.select)

        shoot_or_move_or_not.set_action(self.shoot_or_move_or_not)
        shoot_or_not.set_action(self.shoot_or_not)
        shoot.set_action(self.shoot)
        gen_tank_attack_region.set_action(self.gen_tank_attack_region)
        infer_tank_attack_pos.set_action(self.infer_tank_attack_pos)
        reach_pos_or_not.set_action(self.reach_pos_or_not)
        move.set_action(self.move)
        move_and_shoot.set_action(self.move_and_shoot)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []  # 每次坦克的攻击树的动作都得空的
        return self.bt.run(agent, obj_id)


# class IFVDetectTree:
#     """
#     2022-12-30 毕珊宁
#     战车侦查行为树
#     """
#     def __init__(self, detect_tar=None):
#         """
#
#         :param detect_tar: 侦查点位
#         """
#         self.class_name = 'bt_'
#         self.bt = None
#         self.detect_tar = detect_tar
#         self.actions = []
#         self.init_bt()
#
#     def detect_target_or_not(self, agent, obj_id): #是否有侦查目标
#         if self.detect_tar:
#             return True
#
#     def report_errors(self, agent, obj_id):  # 报错
#         bop = agent.get_bop(obj_id)
#         msg = "没有输入侦查目标点位，请输入".format(agent.color, bop['name'], agent.get_see_scope(bop))
#         self.actions.append(agent.gen_send_string_message(0, msg))  # 发送纯文本聊天信息 0-发给队友，1-发给全部
#         return False
#
#     def action_or_not(self, agent, obj_id):  # 是否可以机动或射击/下车/发射巡飞弹
#         bop = agent.get_bop(obj_id)
#         if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():  # 射击
#             return True
#         elif ActionType.Move in agent.observation["valid_actions"][obj_id].keys():  # 机动
#             return True
#         elif ActionType.GetOff in agent.observation["valid_actions"][obj_id].keys():  # 下车
#             return True
#         elif bop['car']:  # 发射巡飞弹 有属于这个战车的巡飞弹且重型战车有剩余巡飞弹
#             return True
#         else:
#             return False
#
#     def shoot_or_not(self, agent, obj_id):  # 是否射击
#         if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
#             return True
#         else:
#             return False
#
#     def shoot(self, agent, obj_id):  # 射击
#         candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]
#         action = agent.gen_shoot(obj_id, candidate)
#         if action:
#             self.actions.append(action)
#         return True
#
#     def CruiseMissiles_launch_or_not(self): # 是否发射巡飞弹
#
#     def launch_CruiseMissiles(self): # 发射巡飞弹
#
#     def transport_or_not(self): # 是否在运输途中
#
#     def move1(self, agent, obj_id):  # 机动
#         move_action, _, _ = agent.gen_move(obj_id, self.detect_tar)
#         self.actions.append(move_action)
#         return True
#
#     def arrive_transport_spot_or_not(self): # 是否到达运输点位
#
#     def get_off(self, agent, obj_id):  # 下车
#         candidate = agent.observation["valid_actions"][obj_id][ActionType.GetOff]
#         action = agent.gen_get_off(obj_id, candidate)
#         self.actions.append(action)
#         return True
#
#     def arrive_destination_or_not(self, agent, obj_id):
#         bop = agent.get_bop(obj_id)
#         if bop['cur_hex'] == self.detect_tar:  # 如果到达了目标点位
#             print(f"===战车当前位置: {bop['cur_hex']}")
#             print(f"===战车目标位置: {self.detect_tar}")
#             return True
#         return False
#
#     def move2(self, agent, obj_id):  # 机动
#         move_action, _, _ = agent.gen_move(obj_id, self.detect_tar)
#         self.actions.append(move_action)
#     def init_bt(self):
#
#         # 第一个特殊节点:顺序节点
#         order_node_1 = BT()
#         select_node_2 = BT()
#         select_node_3 = BT()
#         order_node_4 = BT()
#         order_node_5 = BT()
#         order_node_6 = BT()
#         select_node_7 = BT()
#         order_node_8 = BT()
#
#         # 是否有侦查目标 报错
#         detect_target_or_not = BT()
#         report_errors =BT()
#
#         # 是否能机动、射击、下车、发送巡飞弹 是否抵达目标点位 机动
#         action_or_not = BT()
#         arrive_destion_or_not = BT()
#         move2 = BT()
#
#         # 是否能射击 射击
#         shoot_or_not = BT()
#         shoot = BT()
#
#         #是否能发射巡飞弹 发射巡飞弹
#         CruiseMissiles_launch_or_not = BT()
#         launch_CruiseMissiles = BT()
#
#         # 是否在运输途中
#         transport_or_not = BT()
#
#         # 机动
#         move1 = BT()
#
#         #是否到达运输点位 下车
#         arrive_transport_spot_or_not = BT()
#         get_off = BT()
#
#         # 连接节点形成树
#         order_node_1.add_child(select_node_2)
#         order_node_1.add_child(select_node_3)
#
#         select_node_2.add_child(detect_target_or_not)
#         select_node_2.add_child(report_errors)
#
#         select_node_3.add_child(action_or_not)
#         select_node_3.add_child(order_node_4)
#         select_node_3.add_child(order_node_5)
#         select_node_3.add_child(order_node_6)
#         select_node_3.add_child(arrive_destion_or_not)
#         select_node_3.add_child(move2)
#
#         order_node_4.add_child(shoot_or_not)
#         order_node_4.add_child(shoot)
#
#         order_node_5.add_child(CruiseMissiles_launch_or_not)
#         order_node_5.add_child(launch_CruiseMissiles)
#
#         order_node_6.add_child(transport_or_not)
#         order_node_6.add_child(select_node_7)
#
#         select_node_7.add_child(order_node_8)
#         select_node_7.add_child(move1)
#
#         order_node_8.add_child(arrive_transport_spot_or_not)
#         order_node_8.add_child(get_off)


class IFVAttackTree:
    """
            2022-12-31 王
            重型战车攻击行为树
    """

    def __init__(self, attack_tar=None):
        """

        :param attack_tar: 攻击目标，缺省是None，缺省时表示攻击目标是所有夺控点
        """
        self.class_name = 'bt_'
        self.attack_tar = attack_tar  # 需要攻击的目标中心（传入的）
        self.IFV_attack_region = None  # 战车的进攻区域
        self.attack_pos = None  # 推理出的攻击位置
        self.attack_over = None  # 为True时候表示需要传入新的防御点位
        self.bt = None
        self.init_bt()
        self.actions = []  # 战车进攻任务输出的行动

    def shoot_or_move_or_gf_or_lm_or_not(self, agent, obj_id):  # 下车和发射巡飞弹也属于移动和机动当中
        if ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot_or_not(self, agent, obj_id):
        if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot(self, agent, obj_id):
        candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]
        action = agent.gen_shoot(obj_id, candidate)
        if action:
            self.actions.append(action)
        return True

    def launch_missile_or_not(self, agent, obj_id):  # 是否可发射巡飞弹
        bop = agent.get_bop(obj_id)
        for passenger_id in bop["passenger_ids"]:
            passenger = agent.get_bop(passenger_id)
            if 4 not in agent.observation["valid_actions"][bop["obj_id"]].keys():
                return False
            if passenger["sub_type"] == BopSubType.CruiseMissle and passenger_id \
                    in [act["target_obj_id"] for act in agent.observation["valid_actions"][bop["obj_id"]][4]]:
                self.actions.append({
                    "actor": agent.seat,
                    "obj_id": bop["obj_id"],
                    "type": ActionType.GetOff,
                    "target_obj_id": passenger_id,
                })
                return True
        return False

    def launch_missile(self, agent, obj_id):  # 发射巡飞弹
        # 在判断是否能发射处已经完成发射巡飞弹操作
        return True

    def transport_or_not(self, agent, obj_id):  # 是否在运输中1？
        if agent.request_for_ride:
            for i in agent.request_for_ride:
                if obj_id == agent.request_for_ride[i]['viches_id']:
                    if agent.request_for_ride[i]['transport_or_not'] == True:
                        return True
        return False

    def arrive_target_or_not(self, agent, obj_id):  # 是否到达运输目标点2？
        bop = agent.get_bop(obj_id)
        for i in agent.request_for_ride:
            if obj_id == agent.request_for_ride[i]['viches_id']:
                if bop['cur_hex'] == agent.request_for_ride[i]['target_pos']:
                    return True
        return False

    def get_off(self, agent, obj_id):  # 下车
        bop = agent.get_bop(obj_id)
        for passenger_id in bop["passenger_ids"]:
            passenger = agent.get_bop(passenger_id)
            if 4 not in agent.observation["valid_actions"][bop["obj_id"]].keys():
                return True
            if passenger["sub_type"] == BopSubType.Infantry and passenger_id \
                    in [act["target_obj_id"] for act in agent.observation["valid_actions"][bop["obj_id"]][4]]:
                self.actions.append({
                    "actor": agent.seat,
                    "obj_id": bop["obj_id"],
                    "type": ActionType.GetOff,
                    "target_obj_id": passenger_id,
                })
        for i in agent.request_for_ride:  # 更改属性
            if obj_id == agent.request_for_ride[i]['viches_id']:
                agent.request_for_ride[i]['transport_or_not'] = False
        return True

    def move1(self, agent, obj_id):
        action = {}
        for i in agent.request_for_ride:  # 更改属性
            if obj_id == agent.request_for_ride[i]['viches_id']:
                action, _, _ = agent.gen_move(obj_id, agent.request_for_ride[i]['target_pos'])  # 运输的机动函数，填入运输的目标点3？
        if action:
            self.actions.append(action)
        return True

    def request_transport_or_not(self, agent, obj_id):  # 是否有运输请求4？
        if agent.request_for_ride:
            for i in agent.request_for_ride:
                if obj_id == agent.request_for_ride[i]['viches_id']:
                    if agent.request_for_ride[i]['accept_or_not'] != True:
                        return True
        return False

    def modify_transport_attribute(self, agent, obj_id):  # 修改相关运输属性5？
        for i in agent.request_for_ride:
            if obj_id == agent.request_for_ride[i]['viches_id']:
                agent.request_for_ride[i]['accept_or_not'] = True
                agent.request_for_ride[i]['transport_or_not'] = True
        return True

    def reach_pos_or_not(self, agent, obj_id):
        if not self.attack_pos:
            return False
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.attack_pos:
            return True
        else:
            return False

    def have_enemy_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if not bop["see_enemy_bop_ids"]:
            return False
        for enemy_id in bop["see_enemy_bop_ids"]:
            enemy = agent.get_bop(enemy_id)
            if enemy["type"] != 3:  # 不是飞机或者巡飞弹
                return True
        return False

    def modify_region(self, agent, obj_id):
        global ifv_a
        ifv_a = None  # 是否区域有改动的标记位
        if not self.IFV_attack_region:
            self.attack_over = True  # 区域为空时标记位，表示需要传入新的进攻点位
        if self.attack_tar:
            return False  # 进攻区域不是夺控点区域时不需要区域的修改
        ifv_attack_region = []
        for i in self.IFV_attack_region:
            if not agent.pos_is_see(i):
                ifv_attack_region.append(i)
        if ifv_attack_region == self.IFV_attack_region:
            ifv_a = False
        else:
            ifv_a = True
            self.IFV_attack_region = ifv_attack_region
        return False

    def infer_attack_pos1(self, agent, obj_id):
        if ifv_a:  # 区域改动的时候重新推理点位
            self.attack_pos = ifv_infer_attack_pos(agent, obj_id, self.IFV_attack_region, self.attack_tar)
        return False

    def move2(self, agent, obj_id):
        if not ifv_a:
            return True
        action, _, _ = agent.gen_move(obj_id, self.attack_pos)  # 需要更换成2022对应的
        if action:
            self.actions.append(action)
        return True

    def gen_attack_region(self, agent, obj_id):
        if self.attack_pos:
            return False
        attack_region = []
        if self.attack_tar:
            attack_region.append(self.attack_tar)
            attack_region += agent.map.get_neighbors(self.attack_tar)
        else:
            attack_region = copy.deepcopy(agent.city_neighbor_3)  # 对应agent中不改变的量要用深拷贝，防备造成干扰，比如区域的修改
        self.IFV_attack_region = attack_region
        return False

    def infer_attack_pos2(self, agent, obj_id):
        # 当到达运输点重新推理一次6？
        if self.attack_pos:
            return False
        else:
            self.attack_pos = ifv_infer_attack_pos(agent, obj_id, self.IFV_attack_region, self.attack_tar)
        return False

    def move3(self, agent, obj_id):
        action, _, _ = agent.gen_move(obj_id, self.attack_pos)  # 运输的机动函数，填入运输的目标点3？
        if action:
            self.actions.append(action)
        return True

    def init_bt(self):

        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        order_node_3 = BT()
        order_node_4 = BT()
        order_node_5 = BT()
        select_node_6 = BT()
        order_node_7 = BT()
        order_node_8 = BT()
        order_node_9 = BT()
        select_node_10 = BT()

        # 是否可机动/射击/下车/发射巡飞弹
        shoot_or_move_or_gf_or_lm_or_not = BT()

        # 是否能射击 射击
        shoot_or_not = BT()
        shoot = BT()

        # 是否能发射巡飞弹 发射巡飞弹
        launch_missile_or_not = BT()
        launch_missile = BT()

        # 是否在运输中
        transport_or_not = BT()

        # 是否到达运输目标点 下车 机动
        arrive_target_or_not = BT()
        get_off = BT()
        move1 = BT()

        # 是否有运输请求 修改相关运输属性
        request_transport_or_not = BT()
        modify_transport_attribute = BT()

        # 是否抵达进攻点位(是否有进攻点位并且抵达进攻点位)
        reach_pos_or_not = BT()

        # 进攻目标区域是否有敌人 修改目标区域 推理进攻点位 机动
        have_enemy_or_not = BT()
        modify_region = BT()
        infer_attack_pos1 = BT()
        move2 = BT()

        # 生成进攻目标区域
        gen_attack_region = BT()

        # 推理进攻点位
        infer_attack_pos2 = BT()

        move3 = BT()

        # 连接节点形成树
        order_node_1.add_child(shoot_or_move_or_gf_or_lm_or_not)
        order_node_1.add_child(select_node_2)

        select_node_2.add_child(order_node_3)
        select_node_2.add_child(order_node_4)
        select_node_2.add_child(order_node_5)
        select_node_2.add_child(order_node_8)
        select_node_2.add_child(order_node_9)
        select_node_2.add_child(gen_attack_region)
        select_node_2.add_child(infer_attack_pos2)
        select_node_2.add_child(move3)

        order_node_3.add_child(shoot_or_not)
        order_node_3.add_child(shoot)

        order_node_4.add_child(launch_missile_or_not)
        order_node_4.add_child(launch_missile)

        order_node_5.add_child(transport_or_not)
        order_node_5.add_child(select_node_6)

        select_node_6.add_child(order_node_7)
        select_node_6.add_child(move1)

        order_node_7.add_child(arrive_target_or_not)
        order_node_7.add_child(get_off)

        order_node_8.add_child(request_transport_or_not)
        order_node_8.add_child(modify_transport_attribute)

        order_node_9.add_child(reach_pos_or_not)
        order_node_9.add_child(select_node_10)

        select_node_10.add_child(have_enemy_or_not)
        select_node_10.add_child(modify_region)
        select_node_10.add_child(infer_attack_pos1)
        select_node_10.add_child(move2)

        # 节点动作的添加
        # 顺序和选择节点的添加
        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        order_node_3.set_action(order_node_3.sequence)
        order_node_4.set_action(order_node_4.sequence)
        order_node_5.set_action(order_node_5.sequence)
        select_node_6.set_action(select_node_6.select)
        order_node_7.set_action(order_node_7.sequence)
        order_node_8.set_action(order_node_8.sequence)
        order_node_9.set_action(order_node_9.sequence)
        select_node_10.set_action(select_node_10.select)

        shoot_or_move_or_gf_or_lm_or_not.set_action(self.shoot_or_move_or_gf_or_lm_or_not)
        shoot_or_not.set_action(self.shoot_or_not)
        shoot.set_action(self.shoot)
        launch_missile_or_not.set_action(self.launch_missile_or_not)
        launch_missile.set_action(self.launch_missile)
        transport_or_not.set_action(self.transport_or_not)
        arrive_target_or_not.set_action(self.arrive_target_or_not)
        get_off.set_action(self.get_off)
        move1.set_action(self.move1)
        request_transport_or_not.set_action(self.request_transport_or_not)
        modify_transport_attribute.set_action(self.modify_transport_attribute)
        reach_pos_or_not.set_action(self.reach_pos_or_not)
        have_enemy_or_not.set_action(self.have_enemy_or_not)
        modify_region.set_action(self.modify_region)
        infer_attack_pos1.set_action(self.infer_attack_pos1)
        move2.set_action(self.move2)
        gen_attack_region.set_action(self.gen_attack_region)
        infer_attack_pos2.set_action(self.infer_attack_pos2)
        move3.set_action(self.move3)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []  # 每次战车的攻击树的动作都得空的
        return self.bt.run(agent, obj_id)


class IFVDetectTree:
    """
    2022-12-30 毕珊宁
    战车侦查行为树
    """

    def __init__(self, detect_tar=None):
        """

        :param detect_tar: 侦查点位
        """
        self.class_name = 'bt_'
        self.bt = None
        self.detect_tar = detect_tar
        self.actions = []
        self.init_bt()

    def detect_target_or_not(self, agent, obj_id):  # 是否有侦查目标
        if self.detect_tar:
            return True
        return False

    def report_errors(self, agent, obj_id):  # 报错
        bop = agent.get_bop(obj_id)
        msg = "{} {} 没有输入侦查目标点位，请输入".format(agent.color, bop['name'])
        self.actions.append(agent.gen_send_string_message(1, msg))  # 发送纯文本聊天信息 0-发给队友，1-发给全部
        return False

    def action_or_not(self, agent, obj_id):  # 是否可以机动或射击/下车/发射巡飞弹
        # 下车和发射巡飞弹也属于移动和机动当中
        if ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot_or_not(self, agent, obj_id):  # 是否射击
        if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot(self, agent, obj_id):  # 射击
        candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]
        action = agent.gen_shoot(obj_id, candidate)
        if action:
            self.actions.append(action)
        return True

    def CruiseMissiles_launch_or_not(self, agent, obj_id):  # 是否发射巡飞弹
        bop = agent.get_bop(obj_id)
        for passenger_id in bop["passenger_ids"]:
            passenger = agent.get_bop(passenger_id)
            if 4 not in agent.observation["valid_actions"][bop["obj_id"]].keys():
                return False
            if passenger["sub_type"] == BopSubType.CruiseMissle and passenger_id \
                    in [act["target_obj_id"] for act in agent.observation["valid_actions"][bop["obj_id"]][4]]:
                self.actions.append({
                    "actor": agent.seat,
                    "obj_id": bop["obj_id"],
                    "type": ActionType.GetOff,
                    "target_obj_id": passenger_id,
                })
                return True
        return False

    def launch_CruiseMissiles(self, agent, obj_id):  # 发射巡飞弹
        # 在判断是否能发射处已经完成发射巡飞弹操作
        return True

    def transport_or_not(self, agent, obj_id):  # 是否在运输途中
        if agent.request_for_ride:
            for i in agent.request_for_ride:
                if obj_id == agent.request_for_ride[i]['viches_id']:
                    if agent.request_for_ride[i]['transport_or_not'] == True:
                        return True
        return False

    def move1(self, agent, obj_id):  # 机动
        action = {}
        for i in agent.request_for_ride:  # 更改属性
            if obj_id == agent.request_for_ride[i]['viches_id']:
                action, _, _ = agent.gen_move(obj_id, agent.request_for_ride[i]['target_pos'])  # 运输的机动函数，填入运输的目标点3？
        if action:
            self.actions.append(action)
        return True

    def arrive_transport_spot_or_not(self, agent, obj_id):  # 是否到达运输点位
        bop = agent.get_bop(obj_id)
        for i in agent.request_for_ride:
            if obj_id == agent.request_for_ride[i]['viches_id']:
                if bop['cur_hex'] == agent.request_for_ride[i]['target_pos']:
                    return True
        return False

    def get_off(self, agent, obj_id):  # 下车
        bop = agent.get_bop(obj_id)
        for passenger_id in bop["passenger_ids"]:
            passenger = agent.get_bop(passenger_id)
            if 4 not in agent.observation["valid_actions"][bop["obj_id"]].keys():
                return True
            if passenger["sub_type"] == BopSubType.Infantry and passenger_id \
                    in [act["target_obj_id"] for act in agent.observation["valid_actions"][bop["obj_id"]][4]]:
                self.actions.append({
                    "actor": agent.seat,
                    "obj_id": bop["obj_id"],
                    "type": ActionType.GetOff,
                    "target_obj_id": passenger_id,
                })
        for i in agent.request_for_ride:  # 更改属性
            if obj_id == agent.request_for_ride[i]['viches_id']:
                agent.request_for_ride[i]['transport_or_not'] = False
        return True

    def arrive_destination_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.detect_tar:  # 如果到达了目标点位
            print(f"===战车当前位置: {bop['cur_hex']}")
            print(f"===战车目标位置: {self.detect_tar}")
            return True
        return False

    def move2(self, agent, obj_id):  # 机动
        move_action, _, _ = agent.gen_move(obj_id, self.detect_tar)
        self.actions.append(move_action)

    def init_bt(self):

        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        select_node_3 = BT()
        select_node_4 = BT()
        order_node_5 = BT()
        order_node_6 = BT()
        order_node_7 = BT()
        select_node_8 = BT()
        order_node_9 = BT()

        # 是否有侦查目标 报错
        detect_target_or_not = BT()
        report_errors = BT()

        # 是否能机动、射击、下车、发送巡飞弹
        action_or_not = BT()

        # 是否抵达目标点位 机动
        arrive_destination_or_not = BT()
        move2 = BT()

        # 是否能射击 射击
        shoot_or_not = BT()
        shoot = BT()

        # 是否能发射巡飞弹 发射巡飞弹
        CruiseMissiles_launch_or_not = BT()
        launch_CruiseMissiles = BT()

        # 是否在运输途中
        transport_or_not = BT()

        # 机动
        move1 = BT()

        # 是否到达运输点位 下车
        arrive_transport_spot_or_not = BT()
        get_off = BT()

        # 连接节点形成树
        order_node_1.add_child(select_node_2)
        order_node_1.add_child(select_node_4)

        select_node_2.add_child(detect_target_or_not)
        select_node_2.add_child(report_errors)

        select_node_3.add_child(action_or_not)

        select_node_4.add_child(order_node_5)
        select_node_4.add_child(order_node_6)
        select_node_4.add_child(order_node_7)
        select_node_4.add_child(arrive_destination_or_not)
        select_node_4.add_child(move2)

        order_node_5.add_child(shoot_or_not)
        order_node_5.add_child(shoot)

        order_node_6.add_child(CruiseMissiles_launch_or_not)
        order_node_6.add_child(launch_CruiseMissiles)

        order_node_7.add_child(transport_or_not)
        order_node_7.add_child(select_node_8)

        select_node_8.add_child(order_node_9)
        select_node_8.add_child(move1)

        order_node_9.add_child(arrive_transport_spot_or_not)
        order_node_9.add_child(get_off)

        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        select_node_3.set_action(select_node_3.select)
        select_node_4.set_action(select_node_4.select)
        order_node_5.set_action(order_node_5.sequence)
        order_node_6.set_action(order_node_6.sequence)
        order_node_7.set_action(order_node_7.sequence)
        select_node_8.set_action(select_node_8.select)
        order_node_9.set_action(order_node_9.sequence)

        detect_target_or_not.set_action(self.detect_target_or_not)
        report_errors.set_action(self.report_errors)
        action_or_not.set_action(self.action_or_not)
        shoot_or_not.set_action(self.shoot_or_not)
        shoot.set_action(self.shoot)
        CruiseMissiles_launch_or_not.set_action(self.CruiseMissiles_launch_or_not)
        launch_CruiseMissiles.set_action(self.launch_CruiseMissiles)
        transport_or_not.set_action(self.transport_or_not)
        move1.set_action(self.move1)
        arrive_transport_spot_or_not.set_action(self.arrive_transport_spot_or_not)
        get_off.set_action(self.get_off)
        arrive_destination_or_not.set_action(self.arrive_destination_or_not)
        move2.set_action(self.move2)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []
        return self.bt.run(agent, obj_id)


class IFVDefenseTree:
    """
    2023-2-16 吴欣娟
    战车防御行为树
    """

    def __init__(self, defense_target):
        self.class_name = 'bt'
        self.bt = None
        self.transport_pos = None  # 用于存储战车运输点位
        self.defense_scope = None  # 用于存储战车防御区域
        self.defense_pos = None  # 用于存储战车防御点位
        self.defense_target = defense_target
        self.init_bt()
        self.actions = []

    def move_shoot_get_off_launch_CruiseMissle_or_not(self, agent, obj_id):
        if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():  # 射击
            return True
        elif ActionType.Move in agent.observation["valid_actions"][obj_id].keys():  # 机动
            return True
        elif ActionType.GetOff in agent.observation["valid_actions"][obj_id].keys():  # 下车
            return True
        # elif ActionType.GetOff in agent.observation["valid_actions"][bop["obj_id"]].keys():  # 发射巡飞弹（感觉不太需要）
        #  for passenger_id in bop["passenger_ids"]:
        #     passenger = agent.get_bop(passenger_id)
        #    if passenger["sub_type"] == BopSubType.CruiseMissle and passenger_id in \
        #           [act["target_obj_id"] for act in agent.observation["valid_actions"][bop["obj_id"]][4]]:
        # 确保巡飞弹算子在下车动作中
        #      return True
        else:
            return False

    def shoot_or_not(self, agent, obj_id):
        if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot(self, agent, obj_id):
        candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]
        self.actions.append(agent.gen_shoot(obj_id, candidate))
        return True

    def launch_CruiseMissle_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if ActionType.GetOff in agent.observation["valid_actions"][bop["obj_id"]].keys():  # 确保算子可以进行下车动作
            for passenger in agent.observation["valid_actions"][bop["obj_id"]][ActionType.GetOff]:
                passenger_bop = agent.get_bop(passenger['target_obj_id'])
                if passenger_bop["sub_type"] == BopSubType.CruiseMissle:
                    # 确保巡飞弹算子在下车动作中
                    return True
        return False

    def launch_CruiseMissle(self, agent, obj_id):
        standby_id = []
        bop = agent.get_bop(obj_id)
        for passenger_id in bop["passenger_ids"]:
            passenger = agent.get_bop(passenger_id)
            if passenger["sub_type"] == BopSubType.CruiseMissle and passenger_id in \
                    [act["target_obj_id"] for act in agent.observation["valid_actions"][bop["obj_id"]][4]]:
                standby_id.append(passenger_id)
        if standby_id:
            target_id = random.choice(standby_id)
            self.actions.append({
                "actor": agent.seat,
                "obj_id": bop["obj_id"],
                "type": ActionType.GetOff,
                "target_obj_id": target_id,
            })
        return True

    def in_transport_or_not(self, agent, obj_id):
        if agent.request_for_ride:
            for i in agent.request_for_ride:
                if obj_id == agent.request_for_ride[i]['viches_id']:
                    if agent.request_for_ride[i]['transport_or_not'] == True:
                        return True
        return False

    def reach_transport_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        for i in agent.request_for_ride:
            if obj_id == agent.request_for_ride[i]['viches_id']:
                if bop['cur_hex'] == agent.request_for_ride[i]['target_pos']:
                    return True
        return False

    def move1(self, agent, obj_id):
        action = {}
        for i in agent.request_for_ride:  # 更改属性
            if obj_id == agent.request_for_ride[i]['viches_id']:
                action, _, _ = agent.gen_move(obj_id, agent.request_for_ride[i]['target_pos'])  # 运输的机动函数，填入运输的目标点3？
        if action:
            self.actions.append(action)
        return True

        # return False

    def get_off(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        for passenger_id in bop["passenger_ids"]:
            passenger = agent.get_bop(passenger_id)
            if 4 not in agent.observation["valid_actions"][bop["obj_id"]].keys():
                return True
            if passenger["sub_type"] == BopSubType.Infantry and passenger_id \
                    in [act["target_obj_id"] for act in agent.observation["valid_actions"][bop["obj_id"]][4]]:
                self.actions.append({
                    "actor": agent.seat,
                    "obj_id": bop["obj_id"],
                    "type": ActionType.GetOff,
                    "target_obj_id": passenger_id,
                })
        for i in agent.request_for_ride:  # 更改属性
            if obj_id == agent.request_for_ride[i]['viches_id']:
                agent.request_for_ride[i]['transport_or_not'] = False
        return True

    def transport_request_or_not(self, agent, obj_id):
        if agent.request_for_ride:
            for i in agent.request_for_ride:
                if obj_id == agent.request_for_ride[i]['viches_id']:
                    if agent.request_for_ride[i]['accept_or_not'] != True:
                        return True
        return False

    def alter_transport_arguments(self, agent, obj_id):
        for i in agent.request_for_ride:
            if obj_id == agent.request_for_ride[i]['viches_id']:
                agent.request_for_ride[i]['accept_or_not'] = True
                agent.request_for_ride[i]['transport_or_not'] = True
        return True

    def create_defense_scope(self, agent, obj_id):
        if self.defense_target:
            self.defense_scope = agent.map.get_neighbors(self.defense_target)
            self.defense_scope.append(self.defense_target)
        else:
            self.defense_scope = agent.city_neighbor_3
        return False

    def infer_defense_pos(self, agent, obj_id):
        # TODO:运输完毕后重新推理防御点位
        others_pos = []
        bop = agent.get_bop(obj_id)
        my_direction = get_direction(agent.city_center, agent.operator_center)
        for id, pos in agent.IFV_defense_pos.items():
            if pos:
                others_pos.append(pos)
        for operator in agent.observation['operators']:
            if operator['color'] == agent.color:
                others_pos.append(operator['cur_hex'])
        for passenger_id in bop['passenger_ids']:
            if passenger_id in agent.request_for_ride.keys():
                if agent.request_for_ride[passenger_id]['target_pos'] == bop['cur_hex'] or (bop['cur_hex'] in others_pos):
                    self.defense_pos = IFV_defense_pos(agent, obj_id, self.defense_scope)
                    agent.IFV_defense_pos[obj_id] = self.defense_pos
                    return False
        if ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            if (agent.map_data[bop['cur_hex'] // 100][bop['cur_hex'] % 100]['cond'] in [1, 2]) and \
                (agent.map.get_distance(get_farrest_hex(bop['cur_hex'], self.defense_scope), bop['cur_hex']) < 20) and \
                (agent.map.get_distance(get_closest_hex(bop['cur_hex'], self.defense_scope), bop['cur_hex']) > 15) and \
                (get_direction(agent.city_center, bop['cur_hex']) == my_direction) and \
                    (bop['cur_hex'] not in others_pos):
                self.defense_pos = bop['cur_hex']
                agent.IFV_defense_pos[obj_id] = self.defense_pos
            else:
                self.defense_pos = IFV_defense_pos(agent, obj_id, self.defense_scope)
                agent.IFV_defense_pos[obj_id] = self.defense_pos
        else:
            defense_pos = agent.IFV_defense_pos[obj_id]
            if (agent.map.get_distance(get_farrest_hex(defense_pos, self.defense_scope), defense_pos) < 20) and \
                    (agent.map.get_distance(get_closest_hex(defense_pos, self.defense_scope), defense_pos) > 15):
                self.defense_pos = defense_pos
            else:
                self.defense_pos = IFV_defense_pos(agent, obj_id, self.defense_scope)
                agent.IFV_defense_pos[obj_id] = self.defense_pos
        return False

    def reach_defense_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.defense_pos or (
                ActionType.Move not in agent.observation["valid_actions"][obj_id].keys()):
            return True
        else:
            return False

    def move2(self, agent, obj_id):
        move_action, _, _ = agent.gen_move(obj_id, self.defense_pos)
        if move_action:
            self.actions.append(move_action)
        return True

    def init_bt(self):
        # 顺序节点和选择节点
        order_node_0 = BT()
        select_node_1 = BT()
        order_node_2 = BT()
        order_node_3 = BT()
        order_node_4 = BT()
        order_node_5 = BT()
        select_node_6 = BT()
        select_node_7 = BT()
        order_node_8 = BT()

        # 是否可机动、射击、下车、发射巡飞弹
        move_shoot_get_off_launch_CruiseMissle_or_not = BT()

        # 是否可以射击 射击
        shoot_or_not = BT()
        shoot = BT()

        # 是否可以发射巡飞弹 发射巡飞弹
        launch_CruiseMissle_or_not = BT()
        launch_CruiseMissle = BT()

        # 是否在运输中 是否到达运输目标点位 机动1 下车
        in_transport_or_not = BT()
        reach_transport_pos_or_not = BT()
        move1 = BT()
        get_off = BT()

        # 是否有运输请求 修改运输相关属性
        transport_request_or_not = BT()
        alter_transport_arguments = BT()

        # 生成防御目标区域 推理防御点位 是否抵达防御点位 机动
        create_defense_scope = BT()
        infer_defense_pos = BT()
        reach_defense_pos_or_not = BT()
        move2 = BT()

        # 连接点形成树
        order_node_0.add_child(move_shoot_get_off_launch_CruiseMissle_or_not)
        order_node_0.add_child(select_node_1)

        select_node_1.add_child(order_node_2)
        select_node_1.add_child(order_node_3)
        select_node_1.add_child(order_node_4)
        select_node_1.add_child(order_node_5)
        select_node_1.add_child(select_node_6)

        order_node_2.add_child(shoot_or_not)
        order_node_2.add_child(shoot)

        order_node_3.add_child(launch_CruiseMissle_or_not)
        order_node_3.add_child(launch_CruiseMissle)

        order_node_4.add_child(in_transport_or_not)
        order_node_4.add_child(select_node_7)

        order_node_5.add_child(transport_request_or_not)
        order_node_5.add_child(alter_transport_arguments)

        select_node_6.add_child(create_defense_scope)
        select_node_6.add_child(infer_defense_pos)
        select_node_6.add_child(reach_defense_pos_or_not)
        select_node_6.add_child(move2)

        select_node_7.add_child(order_node_8)
        select_node_7.add_child(move1)

        order_node_8.add_child(reach_transport_pos_or_not)
        order_node_8.add_child(get_off)

        # 每个节点执行的动作
        # 顺序节点和选择节点
        order_node_0.set_action(order_node_0.sequence)
        select_node_1.set_action(select_node_1.select)
        order_node_2.set_action(order_node_2.sequence)
        order_node_3.set_action(order_node_3.sequence)
        order_node_4.set_action(order_node_4.sequence)
        order_node_5.set_action(order_node_5.sequence)
        select_node_6.set_action(select_node_6.select)
        select_node_7.set_action(select_node_7.select)
        order_node_8.set_action(order_node_8.sequence)

        # 是否可机动、射击、下车、发射巡飞弹
        move_shoot_get_off_launch_CruiseMissle_or_not.set_action(self.move_shoot_get_off_launch_CruiseMissle_or_not)

        # 是否可以射击 射击
        shoot_or_not.set_action(self.shoot_or_not)
        shoot.set_action(self.shoot)

        # 是否可以发射巡飞弹 发射巡飞弹
        launch_CruiseMissle_or_not.set_action(self.launch_CruiseMissle_or_not)
        launch_CruiseMissle.set_action(self.launch_CruiseMissle)

        # 是否在运输中 是否到达运输目标点位 机动1 下车
        in_transport_or_not.set_action(self.in_transport_or_not)
        reach_transport_pos_or_not.set_action(self.reach_transport_pos_or_not)
        move1.set_action(self.move1)
        get_off.set_action(self.get_off)

        # 是否有运输请求 修改运输相关属性
        transport_request_or_not.set_action(self.transport_request_or_not)
        alter_transport_arguments.set_action(self.alter_transport_arguments)

        # 生成防御目标区域 推理防御点位 是否抵达防御点位 机动
        create_defense_scope.set_action(self.create_defense_scope)
        infer_defense_pos.set_action(self.infer_defense_pos)
        reach_defense_pos_or_not.set_action(self.reach_defense_pos_or_not)
        move2.set_action(self.move2)

        self.bt = order_node_0

    def update_bt(self, agent, obj_id):
        self.actions = []
        return self.bt.run(agent, obj_id)



class ArtilleryAttackTree:
    """
    2023-2-7 毕珊宁
    炮兵攻击行为树
    """

    def __init__(self):
        """

        :param detect_tar: 侦查点位
        """
        self.class_name = 'bt_'
        self.bt = None
        self.spot_pos = []  # 间瞄校射位置
        self.actions = []
        self.init_bt()
        self.target_enemy = None  # 推理的间苗目标
        self.target_enemy_id = None  # 推理的间苗目标id

    def cancel_jm(self, agent, obj_id):  # 取消间瞄
        action = agent.gen_cancel_JM_plan(agent)
        if action:
            self.actions.append(obj_id)
        return False

    def need_cancel_jm(self, agent, obj_id):  # 是否取消间瞄
        if agent.observation['jm_points']:
            for jm_point in agent.observation['jm_points']:
                if jm_point['color'] == agent.color and jm_point['pos'] == self.spot_pos:  # 我方的间苗点
                    if jm_point["status"] == 0:  # 正在飞行的
                        if self.target_enemy_id in agent.situation.enemy.keys():  # 取出推测的算子的历史位置
                            enemy_obj_id = self.target_enemy_id
                            historical_positions = list(
                                agent.situation.enemies[enemy_obj_id]['historical_positions'].keys())
                            if self.spot_pos != historical_positions:  # 进行比较 如果位置变了 就取消间苗
                                return True
                            return False

    def cancel_jm_or_not(self, agent, obj_id):  # 是否可以取消间瞄
        if ActionType.CancelJMPlan in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def jm_or_not(self, agent, obj_id):  # 是否可以间瞄
        if ActionType.JMPlan in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def static_target_or_not(self, agent, obj_id):  # 是否有静止的目标
        bop = agent.get_bop(obj_id)
        Infantry_list = []
        Infantry_id = []
        IFV_list = []
        IFV_id = []
        for enemy_obj_id, enemy in agent.situation.enemies.items():
            historical_positions = list(enemy['historical_positions'].keys())
            historical_step = list(enemy['historical_positions'].values())
            if enemy['sub_type'] == BopSubType.IFV:  # 如果敌人类型是战车
                if agent.observation['time']['cur_step'] - historical_step[0][0] > 30:  # 停留时间超过160s
                    IFV_list.append(historical_positions[0])
                    IFV_id.append(enemy_obj_id)
            if enemy['sub_type'] == BopSubType.Infantry:  # 如果敌人类型是步兵
                if agent.observation['time']['cur_step'] - historical_step[0][0] > 160:  # 停留时间超过160s
                    Infantry_list.append(historical_positions[0])
                    Infantry_id.append(enemy_obj_id)
        if Infantry_list:
            distances = [agent.map.get_distance(enemy, bop['cur_hex']) for enemy in Infantry_list]
            index = distances.index(min(distances))
            self.target_enemy = Infantry_list[index]
            self.target_enemy_id = Infantry_id[index]
            return True
        elif IFV_list:
            distances = [agent.map.get_distance(enemy, bop['cur_hex']) for enemy in IFV_list]
            index = distances.index(min(distances))
            self.target_enemy = Infantry_list[index]
            self.target_enemy_id = IFV_id[index]
            return True
        return False

    def key_target_infer(self, agent, obj_id):  # 推理重点目标
        # if [city["flag"] for city in agent.scenario["cities"]] == agent.color: # 判断夺控点是否被我方夺控
        for city in agent.scenario["cities"]:  # 遍历夺控点
            if city['flag'] == agent.color:  # 如果是我方夺控点
                continue
            # 不是我方夺控点
            elif city['flag'] != agent.color:  # 如果是敌方的/没被夺控
                list1 = list(agent.map.get_grid_distance(city["coord"], 0, 5))
                for operator in agent.observation['operators']:  # 遍历所有能看到的我方算子
                    if operator['color'] == agent.color:
                        if operator['cur_hex'] not in list1:  # 如果夺控点周围半径为5内 没有我方算子
                            self.spot_pos.append(city["coord"])
                            return False
            else:
                neighbours = agent.map.get_neighbors(city)
                for neighbour in neighbours:
                    if agent.map.is_in_cover(self, neighbour):
                        self.spot_pos.append(neighbour)
                        return False

    def jmplan1(self, agent, obj_id):  # 间瞄
        candidate = agent.observation["valid_actions"][obj_id][ActionType.JMPlan]
        jm_action, _ = agent.gen_jm_plan(obj_id, candidate, target_pos=self.spot_pos)
        if jm_action:
            self.actions.append(jm_action)
        return False
        # move_action, _, _ = agent.gen_move(obj_id, self.defense_pos)
        # self.actions.append(move_action)

    def jm_target_infer(self, agent, obj_id):  # 推理间瞄目标
        if self.spot_pos:
            self.spot_pos.clear()
            self.spot_pos.append(self.target_enemy)
        return False

    def jmplan2(self, agent, obj_id):  # 间瞄
        candidate = agent.observation["valid_actions"][obj_id][ActionType.JMPlan]
        jm_action, _ = agent.gen_jm_plan(obj_id, candidate, target_pos=self.spot_pos)
        if jm_action:
            self.actions.append(jm_action)
        return False

    def init_bt(self):
        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        select_node_3 = BT()
        order_node_4 = BT()
        select_node_5 = BT()
        select_node_6 = BT()

        # 取消间瞄
        cancel_jm = BT()

        # 是否可以取消间苗
        cancel_jm_or_not = BT()

        # 是否取消间苗
        need_cancel_jm = BT()

        # 是否可以间瞄
        jm_or_not = BT()

        # 是否有静止的目标 推理重点目标 间苗
        static_target_or_not = BT()
        key_target_infer = BT()
        jmplan1 = BT()

        # 推理间苗目标 间苗
        jm_target_infer = BT()
        jmplan2 = BT()

        # 连接节点形成树
        order_node_1.add_child(select_node_2)
        order_node_1.add_child(select_node_3)
        order_node_1.add_child(cancel_jm)

        select_node_2.add_child(cancel_jm_or_not)
        select_node_2.add_child(order_node_4)

        select_node_3.add_child(need_cancel_jm)

        order_node_4.add_child(jm_or_not)
        order_node_4.add_child(select_node_5)
        order_node_4.add_child(select_node_6)

        select_node_5.add_child(static_target_or_not)
        select_node_5.add_child(key_target_infer)
        select_node_5.add_child(jmplan1)

        select_node_6.add_child(jm_target_infer)
        select_node_6.add_child(jmplan2)

        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        select_node_3.set_action(select_node_3.select)
        order_node_4.set_action(order_node_4.sequence)
        select_node_5.set_action(select_node_5.select)
        select_node_6.set_action(select_node_6.select)

        cancel_jm_or_not.set_action(self.cancel_jm_or_not)
        cancel_jm.set_action(self.cancel_jm)
        jm_or_not.set_action(self.jm_or_not)
        need_cancel_jm.set_action(self.need_cancel_jm)
        static_target_or_not.set_action(self.static_target_or_not)
        key_target_infer.set_action(self.key_target_infer)
        jmplan1.set_action(self.jmplan1)
        jm_target_infer.set_action(self.jm_target_infer)
        jmplan2.set_action(self.jmplan2)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []
        return self.bt.run(agent, obj_id)


class MinelayerTree:  # 火箭布雷车的树

    def __init__(self, attack_tar=None):
        """
        :param attack_tar: 攻击目标，缺省是None
        """
        self.class_name = 'bt_'
        self.mine_region = None  # 布雷车的布雷区域
        self.new_mine_pos = None  # 当需要更换布雷位置时前往机动的位置
        self.finish = False  # 为True时表示布雷次数已经用尽并机动到侦察目标位置
        self.mine_pos = None  # 记录的每次推理出的布雷点位
        self.bt = None
        self.init_bt()
        self.actions = []  # 火箭布雷车输出的行动

    def move_or_mine_or_not(self, agent, obj_id):
        if ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        if ActionType.LayMine in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def mine_has_been_exhausted_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop["remaining_mine_count"] == 0:
            return True
        else:
            return False

    def infer_target_pos_and_move(self, agent, obj_id):
        if self.finish:
            return True
        action = {}
        # 机动到距离夺控点中心距离9格的位置
        bop = agent.get_bop(obj_id)
        refer_center = agent.city_center
        hypotenuse = agent.map.get_distance(bop["cur_hex"], refer_center)
        if hypotenuse == 0:  # 三角形相似求出距离9格的圆心
            return
        row0, col0 = divmod(bop["cur_hex"], 100)
        row1, col1 = divmod(refer_center, 100)
        right_angle_side0 = abs(row0 - row1)
        right_angle_side1 = abs(col0 - col1)
        row18_len = int(9 * (right_angle_side0 / hypotenuse)) + 1
        col18_len = int(9 * (right_angle_side1 / hypotenuse)) + 1
        if row0 > row1 and col0 > col1:  # 4个象限的坐标调整
            move_destination_center = (row1 + row18_len) * 100 + col1 + col18_len
        elif row0 > row1 and col0 < col1:
            move_destination_center = (row1 + row18_len) * 100 + col1 - col18_len
        elif row0 < row1 and col0 < col1:
            move_destination_center = (row1 - row18_len) * 100 + col1 - col18_len
        else:
            move_destination_center = (row1 - row18_len) * 100 + col1 + col18_len
        move_type = agent.get_move_type(bop)
        route, _ = agent.map.gen_move_route(bop["cur_hex"], move_destination_center, move_type)
        if route:
            action = {
                "actor": agent.seat,
                "obj_id": obj_id,
                "type": ActionType.Move,
                "move_path": route,
            }
        if action:
            self.actions.append(action)
            self.finish = True
        return True

    def change_mine_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop["cur_hex"] == agent.operator_center:
            return False  # 已经更换一次位置就不继续更换位置了
        # 测试keep属性
        if bop["keep"] != 0 or bop["blood"] <= 1:
            return True
        unthreatening_type = [3, 11, 12, 13, 14]
        enemy_cods = [operator["cur_hex"] for operator in filter(lambda operator: operator['color'] != agent.color and
                                                                                  operator[
                                                                                      "sub_type"] in unthreatening_type,
                                                                 agent.observation['operators'])]
        for i in enemy_cods:
            if agent.map.get_distance(bop["cur_hex"], i) < 12:
                return True
        return False

    def seek_new_mine_pos(self, agent, obj_id):
        if self.new_mine_pos:
            return True
        # 之后设计可以更改为其他的点
        self.new_mine_pos = agent.operator_center
        return True

    def reach_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.new_mine_pos:
            return True
        else:
            return False

    def move(self, agent, obj_id):
        action, _, _ = agent.gen_move(obj_id, self.new_mine_pos)
        if action:
            self.actions.append(action)
        return True

    def gen_mine_region(self, agent, obj_id):
        # 目前是根据agent中的布雷曲线来的，没有区域
        return False

    def infer_mine_pos(self, agent, obj_id):
        # 考虑了布雷后布雷曲线属性点的删除，没有考虑添加。当布雷被敌方打断取消的时候需要将被打断的点添加
        self.mine_pos = None
        for i in agent.minelayer_curve:
            if not i:  # 取出不为空的路径
                continue
            else:
                b = i[0]  # b是输出的一个坐标点
                index = agent.minelayer_curve.index(i)
                agent.minelayer_curve[index].remove(b)  # 在选中的路径中去除被选中的点
                self.mine_pos = b
                break
        return False

    def mine(self, agent, obj_id):
        if ActionType.LayMine not in agent.observation["valid_actions"][obj_id].keys():
            return True
        action = {
            "actor": agent.seat,
            "obj_id": obj_id,
            "type": 20,
            "target_pos": self.mine_pos,
        }
        if self.mine_pos:
            self.actions.append(action)
        return True

    def init_bt(self):

        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        order_node_3 = BT()
        order_node_4 = BT()
        select_node_5 = BT()

        # 是否可机动或布雷
        move_or_mine_or_not = BT()

        # 是否布雷次数已用尽
        mine_has_been_exhausted_or_not = BT()

        # 推理机动目标位置并机动
        infer_target_pos_and_move = BT()

        # 是否需要更换布雷位置
        change_mine_pos_or_not = BT()

        # 找到新的布雷位置
        seek_new_mine_pos = BT()

        # 是否抵达新的布雷位置
        reach_pos_or_not = BT()
        # 机动
        move = BT()

        # 生成布雷的区域
        gen_mine_region = BT()

        # 推理布雷的点位
        infer_mine_pos = BT()

        # 布雷
        mine = BT()

        # 连接节点形成树
        order_node_1.add_child(move_or_mine_or_not)
        order_node_1.add_child(select_node_2)

        select_node_2.add_child(order_node_3)
        select_node_2.add_child(order_node_4)
        select_node_2.add_child(gen_mine_region)
        select_node_2.add_child(infer_mine_pos)
        select_node_2.add_child(mine)

        order_node_3.add_child(mine_has_been_exhausted_or_not)
        order_node_3.add_child(infer_target_pos_and_move)

        order_node_4.add_child(change_mine_pos_or_not)
        order_node_4.add_child(seek_new_mine_pos)
        order_node_4.add_child(select_node_5)

        select_node_5.add_child(reach_pos_or_not)
        select_node_5.add_child(move)

        # 节点动作的添加
        # 顺序和选择节点的添加
        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        order_node_3.set_action(order_node_3.sequence)
        order_node_4.set_action(order_node_4.sequence)
        select_node_5.set_action(select_node_5.select)

        move_or_mine_or_not.set_action(self.move_or_mine_or_not)
        mine_has_been_exhausted_or_not.set_action(self.mine_has_been_exhausted_or_not)
        infer_target_pos_and_move.set_action(self.infer_target_pos_and_move)
        change_mine_pos_or_not.set_action(self.change_mine_pos_or_not)
        seek_new_mine_pos.set_action(self.seek_new_mine_pos)
        reach_pos_or_not.set_action(self.reach_pos_or_not)
        move.set_action(self.move)
        gen_mine_region.set_action(self.gen_mine_region)
        infer_mine_pos.set_action(self.infer_mine_pos)
        mine.set_action(self.mine)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []  # 每次火箭布雷车的动作都得空的
        return self.bt.run(agent, obj_id)


class MineClearanceTree:  # 扫雷车的树

    def __init__(self, clear_tar=None):
        """
        :param clear_tar: 扫雷目标，缺省是None
        """
        self.class_name = 'bt_'
        self.bt = None
        self.access_region = None  # 扫雷车的工作区域
        self.cur_mine = None  # 记录当前正在开辟的雷场
        # self.work_time = None  # 扫雷车扫雷的工作时间
        self.init_bt()
        self.actions = []  # 扫雷车输出的行动

    def move_or_change_state_or_not(self, agent, obj_id):
        if ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def in_minefield_open_way_or_not(self, agent, obj_id):
        # bop = agent.get_bop(obj_id)
        # if self.access_region:
        #     return True
        # for i in agent.access_regions_standby:
        #     if bop['cur_hex'] in i:
        #         self.access_region = i  # 初始态势的时候就在，通路区域就是所在的区域
        #         return True
        if self.access_region:  # 是否有选择过工作区域
            return True
        return False

    def choose_access_region(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        for i in agent.access_regions:
            if not i:
                continue
            if bop['cur_hex'] in i:  # 初始位置就在的情况，可能下面的筛选会影响这里
                self.access_region = i
                index0 = agent.access_regions.index(i)
                agent.access_regions[index0] = []
                return False
        for i in agent.access_regions:
            if not i:
                continue
            self.access_region = i
            index0 = agent.access_regions.index(i)
            agent.access_regions[index0] = []
            return False
        self.access_region = random.choice(agent.access_regions_standby)  # 扫雷车的数量大于通路区域的数量的情况
        return False

    def move_to_access_region(self, agent, obj_id):
        action = None
        bop = agent.get_bop(obj_id)
        city_8 = agent.map.get_grid_distance(agent.city_center, 8, 8)  # 距离夺控点8的圆环
        move_destination = set(self.access_region).intersection(city_8)
        if move_destination:
            move_type = agent.get_move_type(bop)
            route, _ = agent.map.gen_move_route(bop["cur_hex"], list(move_destination)[0], move_type)
            if route:
                action = {
                    "actor": agent.seat,
                    "obj_id": obj_id,
                    "type": ActionType.Move,
                    "move_path": route,
                }
            if action:
                self.actions.append(action)
        return False

    def exist_not_open_way_mine_or_not(self, agent, obj_id):
        # 当敌方的雷都布置完成且工作区域中的雷都开辟通路后返回否
        if agent.observation["landmarks"]["minefields"]:
            for i in agent.observation["landmarks"]["minefields"]:
                if i["hex"] not in self.access_region:
                    continue  # 只负责开辟在工作区域中的雷场
                if not i["roads"]:
                    return True
        return False

    def fit_other_action_or_not(self, agent, obj_id):
        if agent.observation["time"]["cur_step"] > agent.mine_num[0] * 77:
            bop = agent.get_bop(obj_id)
            if bop["move_state"] == 5:  # 处于半速状态下先切换到正常机动状态下再机动
                if ActionType.ChangeState in agent.observation["valid_actions"][obj_id].keys():
                    action = {
                        "actor": agent.seat,
                        "obj_id": obj_id,
                        "type": ActionType.ChangeState,
                        "target_state": 0,
                    }
                    self.actions.append(action)
            return True
        return False

    def fit_move_city_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] in agent.city_neighbor_3:
            return True
        else:
            return False

    def infer_detect_pos_and_move(self, agent, obj_id):
        return False

    def seek_a_city(self, agent, obj_id):
        coord = None
        for i in agent.observation["cities"]:
            if i["flag"] != agent.color:
                coord = i["coord"]
                break
        if coord:
            if agent.gen_move(obj_id, coord):  # 抵达夺控点那一时刻，当前坐标等于夺控点坐标返回None
                action, _, _ = agent.gen_move(obj_id, coord)
                if action:
                    self.actions.append(action)
        return False

    def move_to_city_and_occupy(self, agent, obj_id):
        return False

    def choose_a_mine_to_open_way(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        may_mine = []  # 取出工作区域中所有没有路的雷场找最近的
        for i in agent.observation["landmarks"]["minefields"]:
            if i["hex"] not in self.access_region:
                continue  # 只负责开辟在工作区域中的雷场
            if not i["roads"]:
                may_mine.append(i["hex"])
        distance_list = [agent.map.get_distance(i, bop["cur_hex"]) for i in
                         may_mine]  # 坐标与夺控点的距离列表
        b = distance_list.index(min(distance_list))
        # 距离坐标中的最小值的索引
        self.cur_mine = may_mine[b]  # 移动到新的位置时会进行更新
        return True

    def arrive_neighbor_of_mine(self, agent, obj_id):
        bop = agent.get_bop(obj_id)  # 是否抵达雷场邻域
        nei_mine = agent.map.get_neighbors(self.cur_mine)
        if bop['cur_hex'] in nei_mine:
            return True
        else:
            return False

    def move(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop["move_state"] == 5:  # 处于半速状态下先切换到正常机动状态下再机动
            if ActionType.ChangeState in agent.observation["valid_actions"][obj_id].keys():
                action = {
                    "actor": agent.seat,
                    "obj_id": obj_id,
                    "type": ActionType.ChangeState,
                    "target_state": 0,
                }
                self.actions.append(action)
            return False
        move_type = agent.get_move_type(bop)
        # 可以更改为目标是夺控点，中间要经过雷场
        route, _ = agent.map.gen_move_route(bop["cur_hex"], self.cur_mine, move_type)
        destination = route[-2]  # 机动的目标时雷场的邻域
        action, _, _ = agent.gen_move(obj_id, destination)
        if action:
            self.actions.append(action)
        return False

    def half_speed_state_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop["move_state"] == 5:  # 处于掩蔽状态时
            return True
        else:
            return False

    def change_to_half_speed_state(self, agent, obj_id):
        if ActionType.ChangeState in agent.observation["valid_actions"][obj_id].keys():
            action = {
                "actor": agent.seat,
                "obj_id": obj_id,
                "type": ActionType.ChangeState,
                "target_state": 5,
            }
            self.actions.append(action)
        return False

    def open_a_way(self, agent, obj_id):
        # 现在的开辟方式不会在我方一侧到夺控点形成通路，只开辟的夺控点方向的（可以改为判断雷场在算子的哪个方向，目标点是这个方向的2的距离）
        # direction = get_direction(self.cur_mine, agent.city_center)
        # destination = get_hex_in_direction(self.cur_mine, direction, 1)  # 机动的目标时雷场的邻域
        # route = [self.cur_mine, destination]
        # self.actions.append({
        #         "actor": agent.seat,
        #         "obj_id": obj_id,
        #         "type": ActionType.Move,
        #         "move_path": route,
        #     })
        bop = agent.get_bop(obj_id)
        direction = get_direction(bop['cur_hex'], self.cur_mine)
        destination = get_hex_in_direction(bop['cur_hex'], direction, 2)  # 机动的目标时雷场的邻域
        route = [self.cur_mine, destination]
        self.actions.append({
            "actor": agent.seat,
            "obj_id": obj_id,
            "type": ActionType.Move,
            "move_path": route,
        })
        return False

    def init_bt(self):
        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        order_node_2 = BT()
        select_node_3 = BT()
        select_node_4 = BT()
        order_node_5 = BT()
        select_node_6 = BT()
        select_node_7 = BT()
        select_node_8 = BT()

        # 是否可机动或切换状态
        move_or_change_state_or_not = BT()

        # 是否处于通路区域开辟雷场
        in_minefield_open_way_or_not = BT()

        # 选择需要开辟通路的区域
        choose_access_region = BT()

        # 机动到开辟通路的区域
        move_to_access_region = BT()

        # 通路区域是否有未开辟通路的雷场
        exist_not_open_way_mine_or_not = BT()

        # 是否适合执行其他动作
        fit_other_action_or_not = BT()

        # 当前位置是否适合前往夺控点夺控
        fit_move_city_or_not = BT()

        # 推理侦察的点位并机动
        infer_detect_pos_and_move = BT()

        # 找到我方没有抢占的夺控点
        seek_a_city = BT()

        # 前往夺控点并夺控
        move_to_city_and_occupy = BT()

        # 推理需要开辟通路的雷场
        choose_a_mine_to_open_way = BT()

        # 是否抵达需要开辟通路的雷场邻域
        arrive_neighbor_of_mine = BT()

        # 机动
        move = BT()

        # 是否处于半速状态
        half_speed_state_or_not = BT()

        # 切换到半速状态
        change_to_half_speed_state = BT()

        # 开辟通路
        open_a_way = BT()

        # 连接节点形成树
        order_node_1.add_child(move_or_change_state_or_not)
        order_node_1.add_child(order_node_2)

        order_node_2.add_child(select_node_3)
        order_node_2.add_child(select_node_4)
        order_node_2.add_child(choose_a_mine_to_open_way)
        order_node_2.add_child(select_node_7)
        order_node_2.add_child(select_node_8)
        order_node_2.add_child(open_a_way)

        select_node_3.add_child(in_minefield_open_way_or_not)
        select_node_3.add_child(choose_access_region)
        select_node_3.add_child(move_to_access_region)

        select_node_4.add_child(exist_not_open_way_mine_or_not)
        select_node_4.add_child(order_node_5)

        order_node_5.add_child(fit_other_action_or_not)
        order_node_5.add_child(select_node_6)
        order_node_5.add_child(seek_a_city)
        order_node_5.add_child(move_to_city_and_occupy)

        select_node_6.add_child(fit_move_city_or_not)
        select_node_6.add_child(infer_detect_pos_and_move)

        select_node_7.add_child(arrive_neighbor_of_mine)
        select_node_7.add_child(move)

        select_node_8.add_child(half_speed_state_or_not)
        select_node_8.add_child(change_to_half_speed_state)

        # 节点动作的添加
        # 顺序和选择节点的添加
        order_node_1.set_action(order_node_1.sequence)
        order_node_2.set_action(order_node_2.sequence)
        select_node_3.set_action(select_node_3.select)
        select_node_4.set_action(select_node_4.select)
        order_node_5.set_action(order_node_5.sequence)
        select_node_6.set_action(select_node_6.select)
        select_node_7.set_action(select_node_7.select)
        select_node_8.set_action(select_node_8.select)

        move_or_change_state_or_not.set_action(self.move_or_change_state_or_not)
        in_minefield_open_way_or_not.set_action(self.in_minefield_open_way_or_not)
        choose_access_region.set_action(self.choose_access_region)
        move_to_access_region.set_action(self.move_to_access_region)
        exist_not_open_way_mine_or_not.set_action(self.exist_not_open_way_mine_or_not)
        fit_other_action_or_not.set_action(self.fit_other_action_or_not)
        fit_move_city_or_not.set_action(self.fit_move_city_or_not)
        infer_detect_pos_and_move.set_action(self.infer_detect_pos_and_move)
        seek_a_city.set_action(self.seek_a_city)
        move_to_city_and_occupy.set_action(self.move_to_city_and_occupy)
        choose_a_mine_to_open_way.set_action(self.choose_a_mine_to_open_way)
        arrive_neighbor_of_mine.set_action(self.arrive_neighbor_of_mine)
        move.set_action(self.move)
        half_speed_state_or_not.set_action(self.half_speed_state_or_not)
        change_to_half_speed_state.set_action(self.change_to_half_speed_state)
        open_a_way.set_action(self.open_a_way)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []  # 每次扫雷车的动作都得空的
        return self.bt.run(agent, obj_id)


class AntiAircraftGunTree:  # 防空高炮的树

    def __init__(self, attack_tar=None):
        """
        :param attack_tar: 攻击目标，缺省是None
        """
        self.class_name = 'bt_'
        self.move_pos = None  # 防空高炮的机动目标位置
        self.work_region = None  # 推理点的全体
        self.bt = None
        self.init_bt()
        self.actions = []  # 防控高炮输出的行动

    def move_or_shoot_or_not(self, agent, obj_id):
        if ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot_or_not(self, agent, obj_id):
        if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot(self, agent, obj_id):
        # 没有设计防空高炮的射击函数需要取出candidate
        candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]
        action = agent.gen_shoot(obj_id, candidate)
        if action:
            self.actions.append(action)
        return True

    def infer_move_pos(self, agent, obj_id):
        if self.work_region:  # 可以在区域中多次推理点，目前只推理一次
            return True
        observe_level = []
        for i in agent.AntiAircraftGun_defense_center:
            self.work_region = list(agent.map.get_grid_distance(i, 0, 7))
            region = agent.map.get_grid_distance(i, 10, 13)
            for j in self.work_region:
                num = see_sum(agent, j, region, obj_id)  # 以点位观察区域的数量少，来说明点位不容易被观察
                observe_level.append(num)
            max_see = max(observe_level)  # 对通视的数量进行归一化
            average0 = [v / max_see for v in observe_level]
            average1 = [agent.map.get_distance(v, i) for v in self.work_region]
            max_distance0 = max(average1)  # 对距离中心的坐标距离归一化
            average1 = [v / max_distance0 for v in average1]
            average2 = [-1 if agent.map.is_in_cover(v) else 0 for v in self.work_region]
            average = [average0[v] * 0.4 + average1[v] * 0.2 + average2[v] * 0.4
                       for v in range(len(average0))]  # 加权平均，即最后筛选的标准
            min_average = min(average)
            index0 = average.index(min_average)
            self.move_pos = self.work_region[index0]
            agent.AntiAircraftGun_defense_center.remove(i)
            return True
        i = random.choice(agent.AntiAircraftGun_defense_center_standby)  # 防空高炮的数量大于防守区域的数量的情况
        self.work_region = list(agent.map.get_grid_distance(i, 0, 7))
        region = agent.map.get_grid_distance(i, 10, 13)
        for j in self.work_region:
            num = see_sum(agent, j, region, obj_id)  # 以点位观察区域的数量少，来说明点位不容易被观察
            observe_level.append(num)
        max_see = max(observe_level)  # 对通视的数量进行归一化
        average0 = [v / max_see for v in observe_level]
        average1 = [agent.map.get_distance(v, i) for v in self.work_region]
        max_distance0 = max(average1)  # 对距离中心的坐标距离归一化
        average1 = [v / max_distance0 for v in average1]
        average2 = [-1 if agent.map.is_in_cover(v) else 0 for v in self.work_region]
        average = [average0[v] * 0.4 + average1[v] * 0.2 + average2[v] * 0.4
                   for v in range(len(average0))]  # 加权平均，即最后筛选的标准
        min_average = min(average)
        index0 = average.index(min_average)
        average[index0] = 99
        min_average = min(average)
        index1 = average.index(min_average)
        self.move_pos = self.work_region[index1]
        return True

    def reach_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.move_pos:
            return True
        else:
            return False

    def move(self, agent, obj_id):
        action, _, _ = agent.gen_move(obj_id, self.move_pos)
        if action:
            self.actions.append(action)
        return True

    def init_bt(self):
        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        order_node_3 = BT()
        order_node_4 = BT()
        select_node_5 = BT()

        # 是否可机动或射击
        move_or_shoot_or_not = BT()

        # 是否能射击 射击
        shoot_or_not = BT()
        shoot = BT()

        # 推理防空高炮机动点位
        infer_move_pos = BT()

        # 是否抵达推理点位
        reach_pos_or_not = BT()
        # 机动
        move = BT()

        # 连接节点形成树
        order_node_1.add_child(move_or_shoot_or_not)
        order_node_1.add_child(select_node_2)

        select_node_2.add_child(order_node_3)
        select_node_2.add_child(order_node_4)

        order_node_3.add_child(shoot_or_not)
        order_node_3.add_child(shoot)

        order_node_4.add_child(infer_move_pos)
        order_node_4.add_child(select_node_5)

        select_node_5.add_child(reach_pos_or_not)
        select_node_5.add_child(move)

        # 节点动作的添加
        # 顺序和选择节点的添加
        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        order_node_3.set_action(order_node_3.sequence)
        order_node_4.set_action(order_node_4.sequence)
        select_node_5.set_action(select_node_5.select)

        move_or_shoot_or_not.set_action(self.move_or_shoot_or_not)
        shoot_or_not.set_action(self.shoot_or_not)
        shoot.set_action(self.shoot)
        infer_move_pos.set_action(self.infer_move_pos)
        reach_pos_or_not.set_action(self.reach_pos_or_not)
        move.set_action(self.move)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []  # 每次防空高炮的动作都得空的
        return self.bt.run(agent, obj_id)


class AntiAircraftPlatoonTree:  # 便携防空导弹排的树

    def __init__(self, attack_tar=None):
        """
        :param attack_tar: 攻击目标，缺省是None
        """
        self.class_name = 'bt_'
        self.move_pos = None  # 便携防空导弹排的机动目标位置
        self.work_region = None  # 推理点的全体
        self.bt = None
        self.init_bt()
        self.actions = []  # 便携防空导弹排输出的行动

    def move_or_shoot_or_not(self, agent, obj_id):
        if ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot_or_not(self, agent, obj_id):
        if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot(self, agent, obj_id):
        # 没有设计便携防空导弹排的射击函数需要取出candidate
        candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]
        action = agent.gen_shoot(obj_id, candidate)
        if action:
            self.actions.append(action)
        return True

    def infer_move_pos(self, agent, obj_id):
        # 没有考虑便携防空导弹排有多个的时候分配工作区域的问题
        if self.work_region:  # 可以在区域中多次推理点，目前只推理一次
            return True
        observe_level = []
        i = random.choice(agent.AntiAircraftGun_defense_center_standby)
        self.work_region = list(agent.map.get_grid_distance(i, 0, 7))
        region = agent.map.get_grid_distance(i, 10, 13)
        for j in self.work_region:
            num = see_sum(agent, j, region, obj_id)  # 以点位观察区域的数量少，来说明点位不容易被观察
            observe_level.append(num)
        max_see = max(observe_level)  # 对通视的数量进行归一化
        average0 = [v / max_see for v in observe_level]
        average1 = [agent.map.get_distance(v, i) for v in self.work_region]
        max_distance0 = max(average1)  # 对距离中心的坐标距离归一化
        average1 = [v / max_distance0 for v in average1]
        average2 = [-1 if agent.map.is_in_cover(v) else 0 for v in self.work_region]
        average = [average0[v] * 0.4 + average1[v] * 0.2 + average2[v] * 0.4
                   for v in range(len(average0))]  # 加权平均，即最后筛选的标准
        min_average = min(average)
        index0 = average.index(min_average)
        average[index0] = 99
        min_average = min(average)
        index1 = average.index(min_average)
        self.move_pos = self.work_region[index1]
        return True

    def reach_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.move_pos:
            return True
        else:
            return False

    def move(self, agent, obj_id):
        action, _, _ = agent.gen_move(obj_id, self.move_pos)
        if action:
            self.actions.append(action)
        return True

    def init_bt(self):
        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        order_node_3 = BT()
        order_node_4 = BT()
        select_node_5 = BT()

        # 是否可机动或射击
        move_or_shoot_or_not = BT()

        # 是否能射击 射击
        shoot_or_not = BT()
        shoot = BT()

        # 推理便携防空导弹排机动点位
        infer_move_pos = BT()

        # 是否抵达推理点位
        reach_pos_or_not = BT()
        # 机动
        move = BT()

        # 连接节点形成树
        order_node_1.add_child(move_or_shoot_or_not)
        order_node_1.add_child(select_node_2)

        select_node_2.add_child(order_node_3)
        select_node_2.add_child(order_node_4)

        order_node_3.add_child(shoot_or_not)
        order_node_3.add_child(shoot)

        order_node_4.add_child(infer_move_pos)
        order_node_4.add_child(select_node_5)

        select_node_5.add_child(reach_pos_or_not)
        select_node_5.add_child(move)

        # 节点动作的添加
        # 顺序和选择节点的添加
        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        order_node_3.set_action(order_node_3.sequence)
        order_node_4.set_action(order_node_4.sequence)
        select_node_5.set_action(select_node_5.select)

        move_or_shoot_or_not.set_action(self.move_or_shoot_or_not)
        shoot_or_not.set_action(self.shoot_or_not)
        shoot.set_action(self.shoot)
        infer_move_pos.set_action(self.infer_move_pos)
        reach_pos_or_not.set_action(self.reach_pos_or_not)
        move.set_action(self.move)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []  # 每次便携防空导弹排的动作都得空的
        return self.bt.run(agent, obj_id)




class AntiAircraftVehiTree:  # 车载防空导弹车的树

    def __init__(self, attack_tar=None):
        """
        :param attack_tar: 攻击目标，缺省是None
        """
        self.class_name = 'bt_'
        self.move_pos = None  # 车载防空导弹车的机动目标位置
        self.work_region = None  # 推理点的全体
        self.bt = None
        self.init_bt()
        self.actions = []  # 车载防空导弹车输出的行动

    def move_or_shoot_or_not(self, agent, obj_id):
        if ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot_or_not(self, agent, obj_id):
        if ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot(self, agent, obj_id):
        # 没有设计车载防空导弹车的射击函数需要取出candidate
        candidate = agent.observation["valid_actions"][obj_id][ActionType.Shoot]
        action = agent.gen_shoot(obj_id, candidate)
        if action:
            self.actions.append(action)
        return True

    def infer_move_pos(self, agent, obj_id):
        # 没有考虑车载防空导弹车有多个的时候分配工作区域的问题
        if self.work_region:  # 可以在区域中多次推理点，目前只推理一次
            return True
        observe_level = []
        move_destinations = []  # 记录机动目标的三个点
        i = agent.operator_center
        self.work_region = list(agent.map.get_grid_distance(i, 0, 7)-set(agent.city_neighbor_3))
        region = agent.map.get_grid_distance(i, 10, 13)
        for j in self.work_region:
            num = see_sum(agent, j, region, obj_id)  # 以点位观察区域的数量少，来说明点位不容易被观察
            observe_level.append(num)
        max_see = max(observe_level)  # 对通视的数量进行归一化
        average0 = [v / max_see for v in observe_level]
        average1 = [agent.map.get_distance(v, i) for v in self.work_region]
        max_distance0 = max(average1)  # 对距离中心的坐标距离归一化
        average1 = [v / max_distance0 for v in average1]
        average2 = [-1 if agent.map.is_in_cover(v) else 0 for v in self.work_region]
        average = [average0[v] * 0.4 + average1[v] * 0.2 + average2[v] * 0.4
                   for v in range(len(average0))]  # 加权平均，即最后筛选的标准
        min_average = min(average)
        index0 = average.index(min_average)
        average[index0] = 99
        move_destinations.append(self.work_region[index0])
        min_average = min(average)
        index1 = average.index(min_average)
        average[index1] = 99
        move_destinations.append(self.work_region[index1])
        min_average = min(average)
        index2 = average.index(min_average)
        average[index2] = 99
        move_destinations.append(self.work_region[index2])
        for v in move_destinations:
            if v not in agent.AntiAircraftVehi_history_pos:
                self.move_pos = v
                agent.AntiAircraftVehi_history_pos.append(v)
                break
        return True

    def reach_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.move_pos:
            return True
        else:
            return False

    def move(self, agent, obj_id):
        action, _, _ = agent.gen_move(obj_id, self.move_pos)
        if action:
            self.actions.append(action)
        return True

    def init_bt(self):
        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        order_node_3 = BT()
        order_node_4 = BT()
        select_node_5 = BT()

        # 是否可机动或射击
        move_or_shoot_or_not = BT()

        # 是否能射击 射击
        shoot_or_not = BT()
        shoot = BT()

        # 推理车载防空导弹车机动点位
        infer_move_pos = BT()

        # 是否抵达推理点位
        reach_pos_or_not = BT()
        # 机动
        move = BT()

        # 连接节点形成树
        order_node_1.add_child(move_or_shoot_or_not)
        order_node_1.add_child(select_node_2)

        select_node_2.add_child(order_node_3)
        select_node_2.add_child(order_node_4)

        order_node_3.add_child(shoot_or_not)
        order_node_3.add_child(shoot)

        order_node_4.add_child(infer_move_pos)
        order_node_4.add_child(select_node_5)

        select_node_5.add_child(reach_pos_or_not)
        select_node_5.add_child(move)

        # 节点动作的添加
        # 顺序和选择节点的添加
        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        order_node_3.set_action(order_node_3.sequence)
        order_node_4.set_action(order_node_4.sequence)
        select_node_5.set_action(select_node_5.select)

        move_or_shoot_or_not.set_action(self.move_or_shoot_or_not)
        shoot_or_not.set_action(self.shoot_or_not)
        shoot.set_action(self.shoot)
        infer_move_pos.set_action(self.infer_move_pos)
        reach_pos_or_not.set_action(self.reach_pos_or_not)
        move.set_action(self.move)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []  # 每次车载防空导弹车的动作都得空的
        return self.bt.run(agent, obj_id)


class RadarVehicleTree:  # 炮兵校射雷达车的树

    def __init__(self, other_tar=None):
        """
        :param other_tar: 无
        """
        self.class_name = 'bt_'
        self.other_tar = other_tar
        self.work_region = None  # 推理点的全体
        self.move_pos = None  # 推理出的机动位置
        self.bt = None
        self.init_bt()
        self.actions = []  # 炮兵校射雷达车输出的行动

    def move_or_activate_radar_change_state_or_not(self, agent, obj_id):
        if ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.ActivateRadar in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.ChangeState in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def infer_move_pos(self, agent, obj_id):
        if self.work_region:  # 可以在区域中多次推理点，目前只推理一次
            return False
        observe_level = []
        move_destinations = []  # 记录机动目标的三个点
        i = agent.operator_center
        self.work_region = list(agent.map.get_grid_distance(i, 0, 7)-set(agent.city_neighbor_3))
        region = agent.map.get_grid_distance(i, 10, 13)
        for j in self.work_region:
            num = see_sum(agent, j, region, obj_id)  # 以点位观察区域的数量少，来说明点位不容易被观察
            observe_level.append(num)
        max_see = max(observe_level)  # 对通视的数量进行归一化
        average0 = [v / max_see for v in observe_level]
        average1 = [agent.map.get_distance(v, i) for v in self.work_region]
        max_distance0 = max(average1)  # 对距离中心的坐标距离归一化
        average1 = [v / max_distance0 for v in average1]
        average2 = [-1 if agent.map.is_in_cover(v) else 0 for v in self.work_region]
        average = [average0[v] * 0.4 + average1[v] * 0.2 + average2[v] * 0.4
                   for v in range(len(average0))]  # 加权平均，即最后筛选的标准
        min_average = min(average)
        index0 = average.index(min_average)
        average[index0] = 99
        move_destinations.append(self.work_region[index0])
        min_average = min(average)
        index1 = average.index(min_average)
        average[index1] = 99
        move_destinations.append(self.work_region[index1])
        min_average = min(average)
        index2 = average.index(min_average)
        average[index2] = 99
        move_destinations.append(self.work_region[index2])
        for v in move_destinations:
            if v not in agent.AntiAircraftVehi_history_pos:
                self.move_pos = v
                agent.AntiAircraftVehi_history_pos.append(v)
                break
        return False

    def reach_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.move_pos:
            return True
        else:
            return False

    def cover_state_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop["move_state"] == 4:  # 处于掩蔽状态时
            return True
        else:
            return False

    def activate_radar(self, agent, obj_id):
        if ActionType.ActivateRadar in agent.observation["valid_actions"][obj_id].keys():
            action = {"actor": agent.seat,
                      "obj_id": obj_id,
                      "type": ActionType.ActivateRadar
                      }
            self.actions.append(action)
        return True

    def change_to_cover_state(self, agent, obj_id):
        if ActionType.ChangeState in agent.observation["valid_actions"][obj_id].keys():
            action = {
                "actor": agent.seat,
                "obj_id": obj_id,
                "type": ActionType.ChangeState,
                "target_state": 4,
            }
            self.actions.append(action)
        return True

    def move(self, agent, obj_id):
        action, _, _ = agent.gen_move(obj_id, self.move_pos)
        if action:
            self.actions.append(action)
        return True

    def init_bt(self):

        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        order_node_3 = BT()
        select_node_4 = BT()
        order_node_5 = BT()

        # 是否可机动或切换状态
        move_or_activate_radar_change_state_or_not = BT()

        # 推理机动点位
        infer_move_pos = BT()

        # 是否抵达机动点位
        reach_pos_or_not = BT()

        # 是否处于掩蔽状态 开启校射 切换到掩蔽状态
        cover_state_or_not = BT()
        activate_radar = BT()
        change_to_cover_state = BT()

        # 机动
        move = BT()

        # 连接节点形成树
        order_node_1.add_child(move_or_activate_radar_change_state_or_not)
        order_node_1.add_child(select_node_2)

        select_node_2.add_child(infer_move_pos)
        select_node_2.add_child(order_node_3)
        select_node_2.add_child(move)

        order_node_3.add_child(reach_pos_or_not)
        order_node_3.add_child(select_node_4)

        select_node_4.add_child(order_node_5)
        select_node_4.add_child(change_to_cover_state)

        order_node_5.add_child(cover_state_or_not)
        order_node_5.add_child(activate_radar)

        # 节点动作的添加
        # 顺序和选择节点的添加
        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        order_node_3.set_action(order_node_3.sequence)
        select_node_4.set_action(select_node_4.select)
        order_node_5.set_action(order_node_5.sequence)

        move_or_activate_radar_change_state_or_not.set_action(self.move_or_activate_radar_change_state_or_not)
        infer_move_pos.set_action(self.infer_move_pos)
        reach_pos_or_not.set_action(self.reach_pos_or_not)
        cover_state_or_not.set_action(self.cover_state_or_not)
        activate_radar.set_action(self.activate_radar)
        change_to_cover_state.set_action(self.change_to_cover_state)
        move.set_action(self.move)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []  # 炮兵校射雷达车的动作都得空的
        return self.bt.run(agent, obj_id)


class ReconnaissanceVehicleDetectTree:  # 侦察型战车的侦察树

    def __init__(self, detect_tar=None):
        """
        :param detect_tar: 侦察目标，缺省是None，缺省时表示侦察目标是夺控点区域
        """
        self.class_name = 'bt_'
        self.detect_tar = detect_tar  # 需要侦察的目标中心（传入的）
        self.detect_region = None  # 侦察型战车的侦察区域
        self.detect_pos = None  # 推理出的侦察位置
        self.bt = None
        self.init_bt()
        self.actions = []  # 侦察型战车侦察任务输出的行动

    def move_or_change_state_or_not(self, agent, obj_id):
        if ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def gen_detect_region(self, agent, obj_id):
        if self.detect_region:  # 侦察区域只生成一次
            return False
        if self.detect_tar:  # 传入目标的时候侦察区域是侦察点位附近5的区域
            self.detect_region = list(agent.map.get_grid_distance(self.detect_tar, 0, 5))
        else:
            self.detect_region = agent.city_neighbor_3
        return False

    def infer_detect_pos(self, agent, obj_id):
        if self.detect_pos:
            return False
        bop = agent.get_bop(obj_id)
        if self.detect_tar:
            refer_center = self.detect_tar
        else:
            refer_center = agent.city_center
        hypotenuse = agent.map.get_distance(bop["cur_hex"], refer_center)
        if hypotenuse == 0:  # 三角形相似求出距离4格的圆心
            return
        row0, col0 = divmod(bop["cur_hex"], 100)
        row1, col1 = divmod(refer_center, 100)
        right_angle_side0 = abs(row0 - row1)
        right_angle_side1 = abs(col0 - col1)
        row18_len = int(27 * (right_angle_side0 / hypotenuse)) + 1
        col18_len = int(27 * (right_angle_side1 / hypotenuse)) + 1
        if row0 > row1 and col0 > col1:  # 4个象限的坐标调整
            move_destination_center = (row1 + row18_len) * 100 + col1 + col18_len
        elif row0 > row1 and col0 < col1:
            move_destination_center = (row1 + row18_len) * 100 + col1 - col18_len
        elif row0 < row1 and col0 < col1:
            move_destination_center = (row1 - row18_len) * 100 + col1 - col18_len
        else:
            move_destination_center = (row1 - row18_len) * 100 + col1 + col18_len
        # move_destination_center 可能是无效点超过地图的点
        move_destination_region0 = agent.map.get_grid_distance(move_destination_center, 0, 6)
        poss_observe_level = []
        move_destination_region = []
        for i in move_destination_region0:
            num1 = see_sum(agent, i, self.detect_region, obj_id)
            if num1 > 0:
                move_destination_region.append(i)
                poss_observe_level.append(num1)
        if not poss_observe_level:  # 没有可以通视点需要下达新的作战任务还是按照某种规则移动到一个点位？
            return
        max_see = max(poss_observe_level)  # 对通视的数量进行归一化
        detect_average0 = [i / max_see for i in poss_observe_level]
        detect_average1 = [1 if agent.map.is_in_cover(i) else 0 for i in move_destination_region]
        detect_average = [detect_average0[i] * 0.7 + detect_average1[i] * 0.3
                          for i in range(len(detect_average0))]  # 加权平均，即最后筛选的标准
        move_destinations = []
        max_see_average0 = max(detect_average)  # 极端情况detect_average不足三个时就是重复添加点
        index0 = detect_average.index(max_see_average0)
        detect_average[index0] = 0
        move_destination0 = move_destination_region[index0]
        move_destinations.append(move_destination0)
        max_see_average1 = max(detect_average)
        index1 = detect_average.index(max_see_average1)
        detect_average[index1] = 0
        move_destination1 = move_destination_region[index1]
        move_destinations.append(move_destination1)
        max_see_average2 = max(detect_average)
        index2 = detect_average.index(max_see_average2)
        move_destination2 = move_destination_region[index2]
        move_destinations.append(move_destination2)
        self.detect_pos = random.choice(move_destinations)  # 采用随机，即等可能输出
        return False

    def reach_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.detect_pos:
            return True
        else:
            return False

    def cover_state_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop["move_state"] == 4:  # 处于掩蔽状态时
            return True
        else:
            return False

    def change_to_cover_state(self, agent, obj_id):
        if ActionType.ChangeState in agent.observation["valid_actions"][obj_id].keys():
            action = {
                "actor": agent.seat,
                "obj_id": obj_id,
                "type": ActionType.ChangeState,
                "target_state": 4,
            }
            self.actions.append(action)
        return True

    def move(self, agent, obj_id):
        action, _, _ = agent.gen_move(obj_id, self.detect_pos)
        if action:
            self.actions.append(action)
        return True

    def init_bt(self):

        # 第一个特殊节点:顺序节点
        order_node_1 = BT()
        select_node_2 = BT()
        order_node_3 = BT()
        select_node_4 = BT()

        # 是否可机动或切换状态
        move_or_change_state_or_not = BT()

        # 生成侦察区域
        gen_detect_region = BT()

        # 推理侦察点位
        infer_detect_pos = BT()

        # 是否抵达侦察点位
        reach_pos_or_not = BT()

        # 是否处于掩蔽状态 切换到掩蔽状态
        cover_state_or_not = BT()
        change_to_cover_state = BT()

        # 机动
        move = BT()

        # 连接节点形成树
        order_node_1.add_child(move_or_change_state_or_not)
        order_node_1.add_child(select_node_2)

        select_node_2.add_child(gen_detect_region)
        select_node_2.add_child(infer_detect_pos)
        select_node_2.add_child(order_node_3)
        select_node_2.add_child(move)

        order_node_3.add_child(reach_pos_or_not)
        order_node_3.add_child(select_node_4)

        select_node_4.add_child(cover_state_or_not)
        select_node_4.add_child(change_to_cover_state)

        # 节点动作的添加
        # 顺序和选择节点的添加
        order_node_1.set_action(order_node_1.sequence)
        select_node_2.set_action(select_node_2.select)
        order_node_3.set_action(order_node_3.sequence)
        select_node_4.set_action(select_node_4.select)

        move_or_change_state_or_not.set_action(self.move_or_change_state_or_not)
        gen_detect_region.set_action(self.gen_detect_region)
        infer_detect_pos.set_action(self.infer_detect_pos)
        reach_pos_or_not.set_action(self.reach_pos_or_not)
        cover_state_or_not.set_action(self.cover_state_or_not)
        change_to_cover_state.set_action(self.change_to_cover_state)
        move.set_action(self.move)

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []  # 每次侦察型战车的侦察树的动作都得空的
        return self.bt.run(agent, obj_id)

class UnmannedVehiclesAttackTree:
    """
        2023/07/11
        wang
    """

    def __init__(self, attack_tar=None):
        """
        :param attack_tar: 防守目标，缺省是None，缺省时表示防守目标是所有夺控点
        """
        self.class_name = 'bt_'
        self.attack_tar = attack_tar
        self.bt = None
        self.attack_pos = None  # 用于存储推理出的最佳位置
        self.init_bt()
        self.actions = []

    # def get_bop(self, obj_id, observation):
    #     for bop in observation["operators"]:
    #         if obj_id == bop["obj_id"]:
    #             return bop

    def action_or_not(self, agent, obj_id):
        if ActionType.GuideShoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Move in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def shoot_or_not(self, agent, obj_id):
        if ActionType.GuideShoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        elif ActionType.Shoot in agent.observation["valid_actions"][obj_id].keys():
            return True
        else:
            return False

    def reach_pos_or_not(self, agent, obj_id):
        bop = agent.get_bop(obj_id)
        if bop['cur_hex'] == self.attack_pos:
            return True
        else:
            return False

    def pos_or_not(self, agent, obj_id):
        if self.attack_pos:
            return True
        else:
            return False

    def inference_defend_position(self, agent, obj_id):
        self.attack_pos = uv_infer_coord0(agent, obj_id, self.attack_tar)
        return False

    def change_pos(self, agent, obj_id):
        self.attack_pos = change_ugv_pos(agent, obj_id)
        return True

    def shoot(self, agent, obj_id):
        candidates = []
        if agent.observation['valid_actions'][obj_id].get(ActionType.Shoot):
            candidates += agent.observation['valid_actions'][obj_id].get(ActionType.Shoot)
        elif agent.observation['valid_actions'][obj_id].get(ActionType.GuideShoot):
            candidates += agent.observation['valid_actions'][obj_id].get(ActionType.GuideShoot)
        action = agent.gen_shoot(obj_id, [])
        self.actions.append(action)
        return True

    def move(self, agent, obj_id):
        move_action = agent.gen_move(obj_id, self.attack_pos)
        if move_action:
            self.actions.append(move_action)
        return False

    def init_bt(self):
        # 第一个特殊节点:顺序节点
        order_node_1 = BT()

        # 是否输出行动
        action_or_not = BT()

        # 第二个特殊节点:选择节点
        select_node_2 = BT()

        # 三个顺序节点 两个选择节点
        order_node_3 = BT()
        order_node_4 = BT()
        select_node_5 = BT()
        select_node_6 = BT()
        order_node_7 = BT()

        # 当前能否实施射击 射击节点
        shoot_or_not = BT()
        shoot = BT()

        # 位置是否有值 推理适合防御位置 机动
        pos_or_not = BT()
        inference_defend_position = BT()
        move_for_defend = BT()

        # 是否到达位置 机动
        reach_pos_or_not = BT()
        move_continue = BT()

        # 是否遭遇打击/无人机 更换防御位置 机动
        be_attack_or_not = BT()
        change_position = BT()
        move_for_escape = BT()

        # 连接节点形成树
        order_node_1.add_child(action_or_not)
        order_node_1.add_child(select_node_2)

        select_node_2.add_child(order_node_3)
        select_node_2.add_child(order_node_4)

        order_node_3.add_child(shoot_or_not)
        order_node_3.add_child(shoot)

        order_node_4.add_child(select_node_5)
        order_node_4.add_child(select_node_6)
        order_node_4.add_child(order_node_7)

        select_node_5.add_child(pos_or_not)
        select_node_5.add_child(inference_defend_position)
        select_node_5.add_child(move_for_defend)

        select_node_6.add_child(reach_pos_or_not)
        select_node_6.add_child(move_continue)

        order_node_7.add_child(be_attack_or_not)
        order_node_7.add_child(change_position)
        order_node_7.add_child(move_for_escape)

        # 每个节点执行的动作
        # 顺序节点1
        order_node_1.set_action(order_node_1.sequence)
        # 是否输出行动
        action_or_not.set_action(self.action_or_not)
        # 选择节点2
        select_node_2.set_action(select_node_2.select)

        # 两个顺序节点 两个选择节点
        order_node_3.set_action(order_node_3.sequence)
        order_node_4.set_action(order_node_4.sequence)
        select_node_5.set_action(select_node_5.select)
        select_node_6.set_action(select_node_6.select)
        order_node_7.set_action(order_node_7.sequence)

        # 能否直接射击 射击节点
        shoot_or_not.set_action(self.shoot_or_not)  # 填入 能否射击 函数,返回 success 或 false
        shoot.set_action(self.shoot)  # 填入 射击 函数 返回success

        # 位置是否有值 推理适合防御位置 机动
        pos_or_not.set_action(self.pos_or_not)  # 填入 位置是否有值 函数,有返回success,无返回false
        inference_defend_position.set_action(
            self.inference_defend_position)  # 填入 推理合理防御位置 函数 ,返回false,并将推理出的位置填到self.position中
        move_for_defend.set_action(self.move)  # 填入 机动 函数,调用self.position当作目标点,返回false

        # 是否到达位置 机动
        reach_pos_or_not.set_action(self.reach_pos_or_not)  # 填入 是否到达位置 函数,是返回success,否返回false
        move_continue.set_action(self.move)  # 填入 机动 函数,调用self.position当作目标点,返回false

        # 是否遭遇打击/无人机 更换防御位置 机动
        be_attack_or_not.set_action(judge_unequal_hurt)  # 填入 判断是否遭遇打击/无人机 函数 返回success或false
        change_position.set_action(self.change_pos)  # 填入 推理新防御位置 函数,推理出的位置填到self.position中,并返回success
        move_for_escape.set_action(self.move)  # 填入 机动 函数,调用self.position当作目标点,返回false

        self.bt = order_node_1

    # 更新行为树
    def update_bt(self, agent, obj_id):
        self.actions = []
        return self.bt.run(agent, obj_id)
